1. Hystrix简介
Hystrix是Netflix开源的一款承兑分布式系统的延迟和容错库,目的是用来隔离分布式服务故障。它提供了线程和信号量隔离,以减少不同服务之间的资源竞争带来的相互影响;提供了优雅的降级机制;提供了熔断机制,使得服务可以快速失败,而不是一直阻塞等待服务响应,并能从中快速恢复。Hystrix通过这些机制来阻止级联失败并保证系统弹性、可用。
2、Hystrix的作用
2.1解决的问题:
1: 限制调用分布式服务的资源使用,当某一个调用的服务出现问题不会影响其他服务调用, 通过线程隔离和信号量隔离实现;
2:Hystrix提供了优雅降级机制:超时降级、资源不足时(线程或者信号量)降级,降级后可 以配合降级接口返回托底数据;
3:Hystrix提供了熔断器实现,当失败率达到阈值自动触发降级(如因网络故障或超时造成的 失败率高),熔断器触发的快速失败会进行快速恢复;
4:提供了请求缓存和请求合并实现;
2.2 线程隔离
将Tomcat线程处理的任务转交给Hystrix内部的线程去执行,这样Tomcat线程就可以去 做其他事情。当Hystrix的线程将任务执行完成后,将执行结果返回给Tomcat线程。
1:功能:线程隔离
2:目的:管理线程资源,保证线程不会泛滥最终目的是为了保护服务节点 保障 可用性
3:场景:并发(并发量小一点的场景)的场景 都可以使用
4:走降级的会执行 fallback 方法:
作用:兜底数据/降级 从其他地方给你一个结果,比如redis
当无法获得可用核心线程数时,Hystrix直接在主线程中调用fallback()方法,执行降级逻辑。
2.3 信号量隔离
信号量隔离只是起到一个开关的作用,例如:服务X的信号量大小为10,那么同时只允许10个tomcat线程来访问服务X,其他的请求服务X,其他的请求 将被拒绝,从而达到限流保护的作用
//1 :HystrixCommand 命令所属的组的名称:默认注解方法类的名称
String groupKey() default "";//2 :HystrixCommand 命令的key值,默认值为注解方法的名称
String commandKey() default "";//3 :线程池名称,默认定义为groupKey
String threadPoolKey() default "";
//4 :fallbackMethod 定义回退方法的名称, 此方法必须和hystrix的执行方法在相同类中
String fallbackMethod() default "";
//5 :配置hystrix命令的参数
HystrixProperty[] commandProperties() default {};
//6 :commandProperties配置hystrix依赖的线程池的参数
HystrixProperty[] threadPoolProperties() default {};//5:如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。我们也可以通过此方法定义哪些需要忽略的异常
Class<? extends Throwable>[] ignoreExceptions() default {};//6:定义执行hystrix observable的命令的模式,类型详细见ObservableExecutionMode
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;//7:如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。此方法定义需要抛出的异常
HystrixException[] raiseHystrixExceptions() default {};//8:定义回调方法:但是defaultFallback不能传入参数,返回参数和hystrix的命令兼容
String defaultFallback() default "";
//线程隔离:使用该方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中线程数量的限制。
@GetMapping("/testThread")
@HystrixCommand(
groupKey = "ThreadPoolGroupKey",//HystrixCommand 命令所属的组的名称:默认注解方法类的名称
commandKey = "ThreadPoolCommandKey",//命令的key值,默认值为注解方法的名称
threadPoolKey = "ThreadPoolKey",//线程池名称,默认定义为groupKey
fallbackMethod = "fallbackMethod",//定义回退方法的名称, 此方法必须和hystrix的执行方法在相同类中
// 配置hystrix命令的参数
commandProperties = {
//降级处理超时时间
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1000"),
//该属性用来配置HystrixCommand.run()的执行是否启用超时时间。默认为true
@HystrixProperty(name="execution.timeout.enabled", value="true"),
//执行的隔离策略。默认为THREAD
@HystrixProperty(name="execution.isolation.strategy", value="THREAD")
}
, threadPoolProperties = {//核心线程数
@HystrixProperty(name="coreSize",value="15")
}
)
public String testThread(){
try {
int m = new Random().nextInt(1200);
System.out.println("Thread sleep "+m+" ms");
//与Thread.sleep();是一样的只是对时间进行了封装(枚举)增加可读性
TimeUnit.MILLISECONDS.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Thread Pool";
}
/**
* (信号量隔离):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受到信号量个数的限制。
* @return
*/
@GetMapping("/testSemaphore")
@HystrixCommand(
groupKey = "SemaphoreGroupKey",
commandKey = "SemaphoreCommandKey",
threadPoolKey = "SemaphoreThreadPoolKey",
fallbackMethod = "fallbackMethod",
commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1000"),
@HystrixProperty(name="execution.timeout.enabled", value="true"),
@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE"),
@HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests", value="3"),
@HystrixProperty(name="fallback.isolation.semaphore.maxConcurrentRequests", value="1")
}
)
public String testSemaphore(){
try {
int m = new Random().nextInt(1200);
System.out.println("Thread sleep "+m+" ms");
TimeUnit.MILLISECONDS.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Semaphore";
}
3. 两种模式对比
1:线程池隔离对每个服务,使用单独的线程池
2:信号量隔离使用信号量计数器
3:线程池隔离模式消耗资源大,大量的线程上下文切换
4:信号量隔离模式资源消耗小,只是一个计数器。
线程隔离 | 信号量隔离 | |
线程 | 与调用线程非相同线程 | 与调用线程相同(容器线程) |
开销 | 排队,调度,上下文开销 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(最大线程池大小) | 支持(最大信号量上限) |
四,Hystrix服务降级
当请求出现了异常,超时或者服务不可用等情况下,Hystrix可以自定义降级策略,防止直接返回空或者抛出异常。
当触发服务降级后会执行fallbackMethod();方法;
注:当抛出HystrixBadRequestException异常不会触发降级。
5.服务熔断
熔断也叫过载保护,一般指软件系统中,由于某些原因,服务出现了过载现象,为了防止造成整个系统故障,从而采用一种保护措施,如果某个服务调用慢或者有大量超时,Hystrix熔断该服务的调用,对于后续调用请求不在继续调用目标服务,直接返回,快速释放资源,如果目标服务情况好转则恢复调用。
核心:
1:在滚动时间窗口内,如果没有接收到指定最少请求个数,即使所有请求都失败,也不会打断路由
2:要满足时间\请求数\失败比例三个条件,触发熔断
3:当熔断后,fallback流程由main线程执行,一定时间后重新恢复,尝试执行主业务流程。
@GetMapping("/testCircuitBreaker")
@HystrixCommand(
groupKey = "CircuitBreakerGroupKey",
commandKey = "CircuitBreakerCommandKey",
threadPoolKey = "CircuitBreakerThreadPoolKey",
fallbackMethod = "fallbackMethod",
threadPoolProperties = {
@HystrixProperty(name="coreSize",value="200")
},
commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="1000"),
@HystrixProperty(name="execution.timeout.enabled", value="true"),
@HystrixProperty(name="circuitBreaker.enabled",value="true"),
@HystrixProperty(name="metrics.rollingStats.timeInMilliseconds",value="10000"),
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value="8"),
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="50"),
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="5000")
}
)
public String testCircuitBreaker(){
try {
int value = new Random().nextInt(10);
System.out.println("Random value "+value);
if(value % 3 !=0){
while(true){}
}else{
System.out.println("secuss for "+value);
}
} catch (Exception e) {
e.printStackTrace();
}
return "CircuitBreaker";
}
6.请求缓存
Hystrix可将请求进行缓存,当后续有相同的请求时,直接返回缓存中的响应,从而避免直接对服务进行调用,增加服务的压力
7.请求合并
Hystrix可以在毫秒级别的请求做合并,但是感觉官方并不推荐使用(在官方的Hystrix的流程图中未给出请求合并)
hystrix工作流程图
参考:https://github.com/Netflix/Hystrix/wiki