Spring Cloud Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性。
熔断
熔断机制是应对雪崩效应的一种微服务链路保户机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的相应信息。当检测当该节点微服务调用响应正常后恢复调用链路,熔断机制的注解是@HystrixCommand
“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控,,某个异常条件被触发,直接熔断整个服务。,向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出吊牌用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延。
1 2 3 4 5 6 7
| 第一次正常
第二次提供者异常
提供者多次异常后,断路器打开
后续请求,则直接降级,走备用逻辑。
|
断路器状态转换的逻辑:
1 2 3 4 5 6 7
| 关闭状态:正常情况下,断路器关闭,可以正常请求依赖的服务。
打开状态:当一段时间内,请求失败率达到一定阈值,断路器就会打开。服务请求不会去请求依赖的服务。调用方直接返回。不发生真正的调用。重置时间过后,进入半开模式。
半开状态:断路器打开一段时间后,会自动进入“半开模式”,此时,断路器允许一个服务请求访问依赖的服务。如果此请求成功(或者成功达到一定比例),则关闭断路器,恢复正常访问。否则,则继续保持打开状态。
断路器的打开,能保证服务调用者在调用异常服务时,快速返回结果,避免大量的同步等待,减少服务调用者的资源消耗。并且断路器能在打开一段时间后继续侦测请求执行结果,判断断路器是否能关闭,恢复服务的正常调用。
|
降级
为了在整体资源不够的时候,适当放弃部分服务,将主要的资源投放到核心服务中,待渡过难关之后,再重启已关闭的服务,保证了系统核心服务的稳定。当服务停掉后,自动进入fallback替换主方法。
用fallback方法代替主方法执行并返回结果,对失败的服务进行降级。当调用服务失败次数在一段时间内超过了断路器的阈值时,断路器将打开,不再进行真正的调用,而是快速失败,直接执行fallback逻辑。服务降级保护了服务调用者的逻辑。
1 2 3 4 5 6 7 8 9 10
| 熔断和降级: 共同点: 1、为了防止系统崩溃,保证主要功能的可用性和可靠性。 2、用户体验到某些功能不能用。 不同点: 1、熔断由下级故障触发,主动惹祸。 2、降级由调用方从负荷角度触发,无辜被抛弃。 **Consumer调用Provider服务失败时,走备用逻辑,进行降级,让Consumer业务看起来是正常的,只是数据不完善,但是有响应。 当响应的次数达到熔断的阈值时,不再真正请求Provider,而是直接执行备用逻辑,快速失败。**
|
整合RestTemplate
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| @HystrixCommand(fallbackMethod = "back") public String alive() {
RestTemplate restTemplate = new RestTemplate(); String url ="http://user-provider/User/alive"; String object = restTemplate.getForObject(url, String.class); return object; }
public String back() { return "请求失败~bbb..."; }
|
整合Feign
1 2
| feign.hystrix.enabled=true
|
1 2 3 4 5 6 7 8 9 10
| @RestController public class ConsumerController { @Autowired ConsumerApi consumerSrv;
@GetMapping("/alive") public String alive() { return consumerSrv.isAlive(); } }
|
1 2 3 4 5 6
| @FeignClient(name = "user-provider",fallback = AliveBack.class) public interface ConsumerApi {
@RequestMapping(value = "/User/alive",method = RequestMethod.GET) public String alive(); }
|
1 2 3 4 5 6 7
| @Component public class AliveBack implements ConsumerApi{ @Override public String alive() { return "aaa"; } }
|
FallbackFactory降级的工厂策略。
1
| @FeignClient(name = "user-provider",fallbackFactory = WebError.class)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import feign.hystrix.FallbackFactory; @Component public class WebError implements FallbackFactory<ConsumerApi> {
@Override public ConsumerApi create(Throwable cause) { return new ConsumerApi() { @Override public String alive() { if(cause instanceof InternalServerError) { System.out.println("InternalServerError"); return "远程服务报错"; }else if(cause instanceof RuntimeException) { return "请求时异常:" + cause; }else { return "都算不上"; } } }; } }
|
相关配置
HystrixCommandProperties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds;
private final HystrixProperty metricsRollingStatisticalWindowBuckets;
private final HystrixProperty metricsRollingPercentileEnabled;
private final HystrixProperty circuitBreakerRequestVolumeThreshold;
private final HystrixProperty circuitBreakerSleepWindowInMilliseconds;
private final HystrixProperty circuitBreakerEnabled;
private final HystrixProperty circuitBreakerErrorThresholdPercentage;
private final HystrixProperty circuitBreakerForceOpen;
private final HystrixProperty circuitBreakerForceClosed;
private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests;
private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests;
private final HystrixProperty executionIsolationStrategy;
private final HystrixProperty executionIsolationThreadTimeoutInMilliseconds;
private final HystrixProperty executionIsolationThreadPoolKeyOverride;
private final HystrixProperty fallbackEnabled;
private final HystrixProperty executionIsolationThreadInterruptOnTimeout;
private final HystrixProperty requestLogEnabled;
private final HystrixProperty requestCacheEnabled;
|
隔离策略
Hystrix
提供了两种线程隔离策略:线程池(默认)、信号量。
线程隔离
hystrix将使用独立的线程池对应每一个服务提供者,用于隔离和限制这些服务。在消费者调用提供者时,并发请求受线程池中线程数量的限制。
这样做的好处是严格限制了生产者中,不会因为某一个服务的高并发导致调用的执行,阻塞过长时间,使得某个服务提供者的高延迟或者资源受限只会发生在该服务提供者对应的线程池中,不会影响其他调用服务线程的执行。
HystrixCommand将会在单独的线程上执行。
信号量隔离
其实就是个计数器,通过信号量限制单个服务提供者的并发量,开销相对较小(因为不用那么多线程池),并发请求受到信号量个数的限制。
HystrixCommand将会在调用线程上执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 线程隔离和信号量隔离的区别: 共同点: 都是用来阻断某个服务的请求过多导致其他服务不可用的策略。 不同点: 1.线程隔离通过线程池实现控制服务请求数量 2.信号量隔离通过计数器实现,没有开辟线程空间。
线程隔离: 优点: 1.能够提供拒绝策略 2.线程池内部服务异常隔离 3.能够提供异步请求,通过异步请求服务,完成时调用Tomcat的worker线程的callBack回调,达到解放worker的线程阻塞的目的 缺点: 线程隔离会带来线程开销,比如无其他网络请求的场景,可能会因为开销换隔离得不偿失。
信号量隔离: 优点: 1.不需要消耗线程切换 2.适用于没有网络请求的场景 缺点: 没办法实现拒绝策略
|
线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。
但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务,Hystrix会通过响应中断的方式通知线程立即结束并返回。
1 2
| hystrix.command.default.execution.isolation.strategy=SEMAPHORE
|
Dashboard
1 2
| @EnableHystrixDashboard
|
1 2 3 4 5 6 7 8 9 10 11
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId> spring-cloud-starter-netflix-hystrix-dashboard </artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
|
图形化界面:http://localhost:8080/hystrix