造成服務雪崩的主要原因:
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的時候就是開啟的狀态。