0%

SpringCloud Hystrix

断路器模式源于Martin Fowler的Circuit Breaker一文。“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。

在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),直接切断原来的主逻辑调用。但是,在Hystrix中的断路器除了切断主逻辑的功能之外,还有更复杂的逻辑。

正常情况下,当整个服务环境中,某一个服务提供方由于网络原因、数据库原因或者性能原因等,造成响应很慢的话,调用方就有可能短时间内累计大量的请求线程,最终造成调用方down,甚至整个系统崩溃。而加入hystrix之后,如果hystrix发现某个服务的某台机器调用非常缓慢或者多次调用失败,就会短时间内把这条路断掉,所有的请求都不会再发到这台机器上。

如果某个服务所有的机器都挂了,hystrix会迅速失败,马上返回,保证被调用方不会有大量的线程堆积。

使用eureka时,当一个服务提供方挂掉以后,服务订阅者最长可能30s以后才知道,那这30s就会出现大量的调用失败。如果在系统里面集成了hystrix,就会马上把挂掉的这台服务提供方断路掉,让请求不再转发到这台机器上,大量减少调用失败。
hystrix执行断路操作以后,并不表示这条路就永远断了,而是会一定时间间隔内缓慢尝试去请求这条路,如果能请求成功,断路就会恢复。

有一点需要注意的是hystrix在做断路时,默认所有的调用请求都会放在一个的线程池中进行,线程池的作用很明显,有隔离性。比如gateway,集成了5个子业务系统,可能其中一个系统的调用量非常大,而另外四个系统的调用很小,如果没有线程池的话,显然第一个系统的大量调用会影响到后面四个系统的调用性能。hystrix的线程池和java标准线程池一样,可以配置一些参数:coreSize、maximumSize、maxQueueSize、queueSizeRejectionThreshold、allowMaximumSizeToDivergeFromCoreSize、keepAliveTimeMinutes等,如果某一个子系统的调用量突然激增,超过了线程池的容量,也会迅速失败,直接返回,起到降级和保护系统本身的作用。当然hystrix也支持非线程池的方式,在本地请求线程中做调用,即semaphore模式,官方不建议,除非系统qps真的很大。

Hystrix案例

Feign默认集成了Hystrix。我们可以在上一个moudle service-consumer中增加熔断特性。

application.yml

1
2
3
4
#开启Hystrix
feign:
hystrix:
enabled: true

FeignServiceHystrix.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.linjian.serviceconsumer.service;

import org.springframework.stereotype.Component;

/**
* FeignServiceHystrix
*
* @author jlin
* @date 2019/3/20 11:43
* @Description
*/
@Component
public class FeignServiceHystrix implements FeignExampleService {

@Override
public String hello(String name) {
return "sorry " + name + ",service has fail!";
}
}

FeignExampleService.java修改FeignClient增加fallback熔断处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.linjian.serviceconsumer.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
* FeignExampleService
*
* @author jlin
* @date 2019/3/20 10:40
* @Description
*/
@FeignClient(value = "service-producer", fallback = FeignServiceHystrix.class)
public interface FeignExampleService {

@GetMapping("hello")
public String hello(@RequestParam(value = "name") String name);
}