隔离
隔离是指将系统或资源分隔开,为了在系统发生故障时,减小影响范围,防止雪崩。常见的隔离有,环境隔离,资源隔离,线程隔离,进程隔离,集群隔离,机房隔离,读写隔离,动静隔离,爬虫隔离等等。。本篇主要讲线程隔离。
举个例子,在下单场景下,需要获取商家信息和商品信息,如果商家服务查询数据库发生缓慢情况,也会影响到商品服务,导致整个下单流程不能走完。像这种情况,我们可以采用降级策略,在服务发生故障的时候,进行熔断,返回一些托底数据,阻止雪崩。
我采用的是spring cloud这个框架集合,它提供了spring cloud-hystrix组件。hystrix是Netflix开源的一款容错系统,用来隔离分布式服务故障。
直入话题,下面是Hystrix的实战。
Hystrix提供了俩种隔离方式,信号量隔离和线程池隔离。
线程池隔离
//需引入的依赖包
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
/**
* @author jespon
* @date 2018/4/16 14:44
* @desc 代码方式配置。注意:一个command只能调用一次,不可以写单例,调用只能实例化
*/
public class GetStockCommand extends HystrixCommand<String> {
private StockService stockService;
public GetStockCommand(StockService stockService) {
super(setter());
this.stockService = stockService;
}
private static Setter setter(){
//服务分组名称,相同分局服务聚合
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("stock");
//服务唯一标识
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("getStock");
//线程池名称
HystrixThreadPoolKey poolKey = HystrixThreadPoolKey.Factory.asKey("stock-pool");
HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter()
.withCoreSize(10) //核心线程池大小,核心线程一直存活
.withKeepAliveTimeMinutes(5) //线程池空闲线程的生存时间,超过则销毁
.withMaxQueueSize(10000) //线程池队列大小
.withQueueSizeRejectionThreshold(100); //动态队列大小调整,因为MaxQueueSize不会改变,通过这个属性来限制,当MaxQueueSize=-1(默认值)时,不生效
//线程池隔离方式
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
return HystrixCommand.Setter
.withGroupKey(groupKey)
.andCommandKey(commandKey)
.andThreadPoolKey(poolKey)
.andThreadPoolPropertiesDefaults(threadPoolProperties)
.andCommandPropertiesDefaults(commandProperties);
}
//业务处理,开启一个新线程执行
@Override
protected String run() throws Exception {
return stockService.getStock();
}
//降级处理
@Override
protected String getFallback() {
return "降级处理";
}
}
//调用方代码
public String getStock(){
GetStockCommand getStockCommand = new GetStockCommand(stockService);
String result = getStockCommand.execute();//同步调用
//Future<String> future = getStockCommand.queue();String result = future.get();//异步非堵塞方式调用
//Observable<String> observe = getStockCommand.observe(); observe.asObservable().subscribe((result) -> {System.out.println(result)});//响应式编程
return result;
}
以上是java代码方式配置线程池隔离,这个代码量确实让人有点不敢恭维,于是就有了javanica项目,提供了hystrix的注解配置方式。代码如下
//依赖包
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.12</version>
</dependency>
@HystrixCommand(groupKey = "stock", commandKey = "getStock", threadPoolKey = "stock-pool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "10"),
@HystrixProperty(name = "maxQueueSize",value = "10000"),
@HystrixProperty(name = "keepAliveTimeMinutes",value = "5"),
@HystrixProperty(name = "queueSizeRejectionThreshold",value = "100"),
},
fallbackMethod = "fallbackForGetStock") //降级方法名
public String getStockOne() {
System.out.println("getone");
return stockService.getStock();
}
public String fallbackForGetStock(){
System.out.println("注解配置降级");
return "注解配置fallback";
}
信号量隔离
这里就只写注解配置方式了
//信号量隔离方式,
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"), //并发数量
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "50") // 信号量配置,如果请求超过了并发信号量,则不走fallback,快速响应失败,抛出fallback execution rejected.这个异常
},
fallbackMethod = "fallbackForGetStock")
public String getStockOneForSemaphore() {
return stockFeign.getStock();
}
线程池是控制线程的数量,信号量是用来控制并发数量的。信号量只是限制了总的并发数,服务使用主线程进行同步调用,因此,如果只是限制某个服务的并发数或者不涉及远程调用的情况,可以使用轻量级的信号量隔离。