天天看點

服務雪崩解決方法

造成服務雪崩的主要原因:

1.服務提供者不可用(硬體故障,程式bug,緩存擊穿,使用者大量請求)

2.重試加大流量(使用者重試,代碼邏輯重試)

3.服務調用者不可用(同步等待造成的資源耗盡)

服務降級

逾時降級、資源不足時降級,降級後可以配合降級接口傳回托底資料。實作一個fallback方法,當請求後端 服務出現異常的時候,可以使用fallback方法傳回的值。

pom中加入:

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
           

配置檔案不需要添加什麼

目錄結構:

服務雪崩解決方法

ProductController中的代碼:

@RestController
public class ProductController {
	@Autowired
	private ProductService productService;
	@RequestMapping(value="list",method=RequestMethod.GET)
	public List<Product> listProduct(){
		List<Product> list= this.productService.listProduct();
		return list;
	}
}
           

ProductService中的代碼:

@Service
public class ProductService {
	@Autowired
	private LoadBalancerClient loadBalancerClient;	//ribbon	負載均衡用戶端
	@HystrixCommand(fallbackMethod = "fallback",
			commandProperties = {
		      //預設10秒;如果并發數達到該設定值,請求會被拒絕和抛出異常并且fallback不會被調用。    @HystrixProperty(name=HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="15")
		    })
	public List<Product> listProduct(){
		//擷取eurekaone-provider用戶端
		ServiceInstance	si=loadBalancerClient.choose("e-book-product");
		StringBuffer sb = new StringBuffer("");
		sb.append("http://");
		//擷取ip位址
		sb.append(si.getHost());
		sb.append(":");
		//擷取端口号
		sb.append(si.getPort());
		sb.append("/product/list");
		System.out.println(sb.toString());  
		RestTemplate rt = new RestTemplate();
		//對象為Product集合
		ParameterizedTypeReference<List<Product>> typeRef =new ParameterizedTypeReference<List<Product>>(){};
		//參數是浏覽器位址,請求方法,和轉化為的對象
		ResponseEntity<List<Product>> resp= rt.exchange(sb.toString(),HttpMethod.GET, null, typeRef);
		List<Product> plist = resp.getBody();
		return plist;
	}
	public List<Product> fallback() {
		List<Product> list = new ArrayList<Product>();
		list.add(new Product(-1,"fallback"));
		return list;
	}
}
           

使用降級,我們隻是多添加了一個注解,然後建立fallback的方法,如果并發達到15的時候或則product沒有,就會自動降級。當我們關閉product的時候:

服務雪崩解決方法

它就會自動降級,執行fallback方法。

請求緩存

pom檔案中加入:

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
           

這回的pom檔案多加入了redis的依賴。

配置檔案中加入:

#程式啟動時建立的緩存名稱
#spring.cache.cache-names=com.agan.book
# Redis資料索引(預設為0)
spring.redis.database=1
#Redis伺服器位址
spring.redis.host=192.168.23.129
#Redis伺服器連接配接端口
spring.redis.port=6379
#Redis伺服器連接配接密碼(預設為空)
spring.redis.password=
#連接配接池最大連接配接數(負值表示沒有限制)
spring.redis.pool.max-active=100
#連接配接池最大阻塞等待時間(負值表示沒有限制)
spring.redis.pool.max-wait=3000
#連接配接池最大空閉連接配接數
spring.redis.pool.max-idle=200
#連接配接漢最小空閑連接配接數
spring.redis.pool.min-idle=50
#連接配接逾時時間(毫秒)
spring.redis.pool.timeout=600
           

注意你的電腦虛拟機Linux中必須要安裝了redis并且注意伺服器的位址。

目錄檔案:

服務雪崩解決方法

ProductController代碼:

@RestController
public class ProductController {
	@Autowired
	private ProductService productService;
	@RequestMapping(value="list",method=RequestMethod.GET)
	public List<Product> listProduct(){
		List<Product> list= this.productService.listProduct();
		return list;
	}
	@RequestMapping(value="get",method=RequestMethod.GET)
	public Product get(Integer id) {
		return this.productService.get(id);
	}
	@RequestMapping(value="del",method=RequestMethod.GET)
	public void del(Integer id) {
		 this.productService.del(id);
	}
}

           

這裡的代碼主要是加入了get和del。

ProductService加入代碼:

@Cacheable(key="'product' + #id")
	public Product get(Integer id) {
		System.out.println("=========get============"+id);
		return new Product(id,"test6");
	}
	@CacheEvict(key="'product' + #id")
	public void del(Integer id) {
		System.out.println("=========del============"+id);
	}
           

在上面降級的基礎上,加入了以上代碼。

測試結果:

服務雪崩解決方法

我開始将id為2的删除掉,然後get?id=2兩次,發現隻有第一次列印,第二次沒有列印,說明第二次是緩存。

伺服器中,注意要選擇資料庫

服務雪崩解決方法

請求合并

在一定時間内将多個請求合并為一個請求。

在pom中加入:

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
			<groupId>com.netflix.hystrix</groupId>
			<artifactId>hystrix-javanica</artifactId>
			<version>RELEASE</version>
</dependency>
           

配置檔案不需要添加。

目錄結構:

服務雪崩解決方法

ProductController代碼

@RestController
public class ProductController {
	@Autowired
	private ProductService productService;
	@RequestMapping(value="getproduct",method=RequestMethod.GET)
	public void getproduct() throws InterruptedException, ExecutionException{
		Future<Product> p1= this.productService.getProduct(1);
		Future<Product> p2= this.productService.getProduct(2);
		Future<Product> p3= this.productService.getProduct(3);
		System.out.println(p1.get().toString());
		System.out.println(p2.get().toString());
		System.out.println(p3.get().toString());
	} 
}
           

ProductService代碼:

@Service
public class ProductService {
	//利用hystrix合并請求  
    @HystrixCollapser(batchMethod = "batchProduct", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,  
    		collapserProperties = {  
    		//請求時間間隔在50ms之内的請求會被合并為一個請求
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
            //設定觸發批處理執行之前,在批進行中允許的最大請求數。
            @HystrixProperty(name = "maxRequestsInBatch", value = "200"),  
    })
	public Future<Product> getProduct(Integer id) {
		System.out.println("---------"+ id + "---------");
		return null;
	}
	@HystrixCommand
	public List<Product> batchProduct(List<Integer> ids) {
		for (Integer id:ids) {
			System.out.println(id);
		}
		List<Product> list= new  ArrayList<Product>();
		list.add(new Product(1,"test1無的放矢付撒發付付撒奧過過過過過過過過"));
		list.add(new Product(2,"test2無的放矢付撒發付付撒奧過過過過過過過過"));
		list.add(new Product(3,"test3無的放矢付撒發付付撒奧過過過過過過過過"));
		list.add(new Product(4,"44444444444444444444444444444444444444444"));
		return list;
	}
}
           

測試結果:

服務雪崩解決方法

并沒有列印---------"+ id + "---------說明請求合并了。

服務熔斷

當失敗率達到阙值自動觸發降級,熔斷器觸發的快速失敗會進行快速恢複。

在pom中加入:

<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
           

目錄結構:

服務雪崩解決方法

ProductController代碼:

@RestController
public class ProductController {
	@Autowired
	private ProductService productService;
	@RequestMapping(value="list",method=RequestMethod.GET)
	public List<Product> listProduct(@RequestParam("n") Integer n){
		List<Product> list= this.productService.listProduct(n);
		return list;
	}
}
           

ProductService代碼:

@Service
public class ProductService {
	@Autowired
	private LoadBalancerClient loadBalancerClient;	//ribbon	負載均衡用戶端
	@HystrixCommand(fallbackMethod = "fallback",
			commandProperties = {
			  //預設20個;10s内請求數大于20個時就啟動熔斷器,當請求符合熔斷條件時将觸發getFallback()。
		      @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value="10"),
		      //請求錯誤率大于50%時就熔斷,然後for循環發起請求,當請求符合熔斷條件時将觸發getFallback()。      @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value="50"),
		      //預設5秒;熔斷多少秒後去嘗試請求      @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="5000"),
		    })
	public List<Product> listProduct(int n){
		System.out.println(n);
		if (n==1) {
			throw new RuntimeException();
		}
		//擷取eurekaone-provider用戶端
		ServiceInstance	si=loadBalancerClient.choose("e-book-product");
		StringBuffer sb = new StringBuffer("");
		sb.append("http://");
		//擷取ip位址
		sb.append(si.getHost());
		sb.append(":");
		//擷取端口号
		sb.append(si.getPort());
		sb.append("/product/list");
		System.out.println(sb.toString());  
		RestTemplate rt = new RestTemplate();
		//對象為Product集合
		ParameterizedTypeReference<List<Product>> typeRef =new ParameterizedTypeReference<List<Product>>(){};
		//參數是浏覽器位址,請求方法,和轉化為的對象
		ResponseEntity<List<Product>> resp= rt.exchange(sb.toString(),HttpMethod.GET, null, typeRef);
		List<Product> plist = resp.getBody();
		return plist;
	}
	public List<Product> fallback(int n) {
		List<Product> list = new ArrayList<Product>();
		list.add(new Product(-1,"fallback"));
		return list;
	}
}
           

主要就是改變了注解。

測試:

服務雪崩解決方法
服務雪崩解決方法

當n為1的時候為關閉的狀态,n不為1的時候就是開啟的狀态。

繼續閱讀