在上一篇文章中已經說明了服務雪崩是什麼,然後還列出了服務雪崩的幾種解決方法,包括:降級服務、請求緩存、請求合并、服務熔斷這4種方法。
上一篇服務雪崩
然後這一篇列出其他的幾種方法。
線程池隔離
不使用線程池隔離會互相影響,使用線程池隔離互不影響。
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="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(groupKey="e-book-product", commandKey = "listProduct",
threadPoolKey="e-book-product",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),//線程池大小
@HystrixProperty(name = "maxQueueSize", value = "100"),//最大隊列長度
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//線程存活時間
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒絕請求
},
fallbackMethod = "fallback")
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;
}
}
測試結果:
注解解釋:
groupKey=“e-book-product”, commandKey = “listProduct”,threadPoolKey=“e-book-product”,每一個線程池對應一個product,commandKey 是接口名,
threadPoolKey是product名稱。
信号量隔離
當請求量過大的時候就會進入fallback中。
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="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 = { @HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value="SEMAPHORE"),// 信号量 隔離
@HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="100")//信号量最大并發度
})
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;
}
}
與線程池隔離隻是改變了一下注解。注解注意的地方是最大信号量和value的值。
測試結果:
線程池隔離和信号量隔離差別,使用場景
Feign服務降級
pom中加入:
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>e-book-product-api</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
配置檔案加入:
#Feign預設是不開啟Hystrix
feign.hystrix.enabled=true
目錄結構:
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代碼:
@FeignClient(name="e-book-product",fallback=ProductServiceFallback.class)
public interface ProductService {
//無參
@RequestMapping(value="product/list",method=RequestMethod.GET)
public List<Product> listProduct();
}
ProductServiceFallback代碼:
@Component
public class ProductServiceFallback implements ProductService{
@Override
public List<Product> listProduct() {
List<Product> list = new ArrayList<Product>();
list.add(new Product(-1,"fallback"));
return list;
}
}
測試結果:
當consumer和product都啟動的時候:
當關閉product的時候:
就會進入fallback方法裡面。
Feign服務降級後的異常記錄
上面的例子是Feign服務降級,然後我們可以把服務降級的異常記錄下來。
這個的pom檔案和配置檔案與上面是一樣的。
目錄結構:
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代碼:
@FeignClient(name="e-book-product",fallbackFactory=ProductServiceFallbackFactory.class)
public interface ProductService {
//無參
@RequestMapping(value="product/list",method=RequestMethod.GET)
public List<Product> listProduct();
}
ProductServiceFallbackFactory代碼:
@Component
public class ProductServiceFallbackFactory implements FallbackFactory<ProductService>{
private Logger logger = LoggerFactory.getLogger(ProductServiceFallbackFactory.class);
@Override
public ProductService create(final Throwable arg0) {
return new ProductService() {
@Override
public List<Product> listProduct() {
logger.warn("fallback exception:",arg0);
List<Product> list = new ArrayList<Product>();
list.add(new Product(-1,"fallback"));
return list;
}
};
}
}
這裡主要繼承的接口變為了FallbackFactory
然後加入了private Logger logger = LoggerFactory.getLogger(ProductServiceFallbackFactory.class);
和logger.warn(“fallback exception:”,arg0);
啟動consumer和product測試:
當關閉product時:
就會列印出異常日志。