一、Hystrix目的
隔离:防止服务级联失败;业务处理隔离(没有远程调用)
熔断:超时、失败率、信号量
降解:返回托底数据
二、Hystrix原理
1. 执行流程
step1:每次调用创建一个新的HystrixCommand,把依赖调用封装在run()方法中。
step2:执行execute()/queue做同步或异步调用。
step3:判断熔断器(circuit-breaker)是否打开,如果打开跳到步骤8,进行降级策略,如果关闭进入步骤。
step4:判断线程池/队列/信号量是否跑满,如果跑满进入降级步骤8,否则继续后续步骤。
step5:调用HystrixCommand的run方法。运行依赖逻辑
step5a:依赖逻辑调用超时,进入步骤8。
step6:判断逻辑是否调用成功
step6a:返回成功调用结果
step6b:调用出错,进入步骤8。
step7:计算熔断器状态,所有的运行状态(成功, 失败, 拒绝,超时)上报给熔断器,用于统计从而判断熔断器状态。
step8:getFallback()降级逻辑。
以下四种情况将触发getFallback调用:
(1):run()方法抛出非HystrixBadRequestException异常
(2):run()方法调用超时
(3):熔断器开启拦截调用
(4):线程池/队列/信号量是否跑满
step8a:没有实现getFallback的Command将直接抛出异常
step8b:fallback降级逻辑调用成功直接返回
step8c:降级逻辑调用失败抛出异常
step9:返回执行成功结果
2. 执行方式
package com.netflix.hystrix;
import java.util.concurrent.Future;
import rx.Observable;
import rx.schedulers.Schedulers;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.netflix.hystrix.exception.HystrixRuntimeException;
public interface HystrixExecutable<R> extends HystrixInvokable<R> {
/**
* 同步执行命令:当前线程需该命令执行完成后才能往下执行(阻塞)
* @return R 执行结果
* @throws HystrixRuntimeException 发生错误或fallback没有挽回,则抛出异常
* @throws HystrixBadRequestException 请求参数无效异常
*/
public R execute();
/**
* 异步执行命令:当前线程无需该命令执行完成后才能往下执行(非阻塞)
* 异步返回Future来获取结果
* 注意:如果不是线程池隔离,则与execute()执行相同都具有阻塞
* @return Future<R> 执行结果
* @throws HystrixRuntimeException 发生错误或fallback没有挽回,则抛出异常
* @throws HystrixBadRequestException 请求参数无效异常
*/
public Future<R> queue();
/**
* 响应式异步执行,Observable获取执行结果
* 立即获取:与execute()、queue()相同
* 延迟获取:与toObservable()相同
* @return Observable<R> 返回执行结果(包括进入fallback)
* @throws HystrixRuntimeException 发生错误或fallback没有挽回(熔断器打开,队列饱和),则抛出异常
* @throws HystrixBadRequestException 请求参数无效异常
* @throws IllegalStateException 多次调用
*/
public Observable<R> observe();
}
package com.netflix.hystrix;
import rx.Observable;
import rx.schedulers.Schedulers;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.netflix.hystrix.exception.HystrixRuntimeException;
public interface HystrixObservable<R> extends HystrixInvokable<R> {
/**
* 响应式异步执行,Observable获取执行结果
* 立即获取:与execute()、queue()相同
* 延迟获取:与toObservable()相同
* @return Observable<R> 返回执行结果(包括进入fallback)
* @throws HystrixRuntimeException 发生错误或fallback没有挽回(熔断器打开,队列饱和),则抛出异常
* @throws HystrixBadRequestException 请求参数无效异常
* @throws IllegalStateException 多次调用
*/
public Observable<R> observe();
/**
* Used for asynchronous execution of command with a callback by subscribing to the {@link Observable}.
* 响应式异步执行,Observable获取执行结果
* 延迟获取
* 立即获取:observe()
* @return Observable<R> 返回执行结果(包括进入fallback)
* @throws HystrixRuntimeException 发生错误或fallback没有挽回(熔断器打开,队列饱和),则抛出异常
* @throws HystrixBadRequestException 请求参数无效异常
* @throws IllegalStateException 多次调用
*/
public Observable<R> toObservable();
}
总结执行方法:
方法
类
说明
R execute()
HystrixCommand
同步执行:当前线程需该命令执行完成后才能往下执行(阻塞)
Future<R> queue()
异步执行:当前线程无需该命令执行完成能往下执行(非阻塞)
Observable<R> observe()
HystrixObservableCommand
响应式异步执行:立即获取
Observable<R> toObservable()
响应式异步执行:惰性获取
3. 隔离策略
线程池(THREAD):默认,池与主线程互不影响、远程服务调用互不影响;开销大
信号量(SEMAPHORE):只限制总并发数,主线程同步调用;轻量级
类型 优点 不足 适用
线程
支持排队和超时;
支持异步调用
线程调用和切换产生额外开销 不受信客户(比如第三方服务稳定性是无法推测的)
信号量 轻量且无额外开销
不支持任务排队和超时;
不支持异步
受信客户、高频高速调用服务(网关、cache)
三、实现实例
1. 自定义GetStockServiceCommand类
package com.common.instance.demo.hystrix;
import com.common.instance.demo.entity.WcPendantTab;
import com.common.instance.demo.service.WcPendantTabService;
import com.netflix.hystrix.*;
import java.util.List;
/**
* @description Hystrix使用
* @author tcm
* @version 1.0.0
* @date 2021/9/27 11:00
**/
public class GetStockServiceCommand extends HystrixCommand<List<WcPendantTab>> {
private WcPendantTabService wcPendantTabService;
private WcPendantTab tab;
protected GetStockServiceCommand(WcPendantTabService wcPendantTabService, WcPendantTab tab) {
super(setter());
this.wcPendantTabService = wcPendantTabService;
this.tab = tab;
}
private static Setter setter(){
// 服务分组,默认一个服务名一个组
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("stock");
// 服务标识,默认当前执行的方法名称
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("getStock");
// 线程池名称,相同线程池名称的线程池是同一个
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("stock-pool");
// 线程池配置
HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter()
// 最大并发执行数,默认10
.withCoreSize(10)
// 存活时间(控制一个线程从实用完成到被释放的时间),默认1min
.withKeepAliveTimeMinutes(5)
// BlockingQueue的最大长度,正数:队列将从同步队列改为阻塞队列,默认-1
.withMaxQueueSize(Integer.MAX_VALUE)
// 拒绝请求的临界值,默认5
.withQueueSizeRejectionThreshold(100);
// 命令属性配置
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
// 隔离策略:THREAD(线程池_默认)、SEMAPHORE(信号量)
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
return Setter
.withGroupKey(groupKey)
.andCommandKey(commandKey)
.andThreadPoolKey(threadPoolKey)
.andThreadPoolPropertiesDefaults(threadPoolProperties)
.andCommandPropertiesDefaults(commandProperties);
}
// 执行任务
@Override
protected List<WcPendantTab> run() throws Exception {
return wcPendantTabService.queryAll(this.tab);
}
// fallback
@Override
protected List<WcPendantTab> getFallback() {
return null;
}
}
2. 调用Hystrix命令
package com.common.instance.demo.hystrix;
import com.common.instance.demo.core.Response;
import com.common.instance.demo.entity.WcPendantTab;
import com.common.instance.demo.service.WcPendantTabService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @description Hystrix测试
* @author tcm
* @version 1.0.0
* @date 2021/9/27 17:47
**/
@RestController
@RequestMapping("/hystrix")
@Api(tags = "Hystrix测试")
public class HystrixController {
@Resource
private WcPendantTabService wcPendantTabService;
@PostMapping("/hystrixCommand")
@ApiOperation("测试HystrixCommand")
public Response<List<WcPendantTab>> testHystrixCommand(WcPendantTab tab) {
GetStockServiceCommand getStockServiceCommand = new GetStockServiceCommand(wcPendantTabService, tab);
List<WcPendantTab> result = getStockServiceCommand.execute();
return Response.success(result);
}
}
3. 压测结果
普通接口测试
Hytrix线程池隔离
普通接口测试
Hytrix线程池隔离
两个压测的业务逻辑:800W条数据查询相同的数据,没有缓存配置;调用接口时,CPU=6%、内存=63%、磁盘=1%,网络=0%。压测条件相同,但是Hytrix线程池隔离的吞吐量5025/s比普通接口4054/s高出1000,主要是线程池提高了并发量。