熔断器
在微服务架构中,我们将系统拆分成了很多服务单元,各单元的应用通过服务注册与订阅的方式相互依赖。 由于单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。
为了解决这样的问题,产生了断路器
等一系列的服务保护机制。
断路器
本身是一种开关装置,用于电路上保护线路过载,当线路中有电器发生短路时,断路器能够及时切断故障电路,放置发生过载、发热甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障时,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间的等待
。这样就不会是的线程因为调用故障服务被长时间占用不释放,避免了故障在分布式系统中蔓延。
Spring Cloud Hystrix实现了断路器、线程隔离等一些列的服务保护功能。它具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控
等强大功能。
快速入门
在开始使用Hystrix
实现断路器之前,我们先用之前实现的一些内容作为基础,构建一个如下的服务调用关系。
Eureka注册中心
:peer1:1111,peer2:1112
服务提供者(CLOUD-EUREKA-CLIENT)
:启动两个服务实例localhost:8080, localhost8081
服务消费者(RIBBON-CLIENT)
:使用Ribbon实现的服务消费,localhost:9000
正常启动
当所有2个服务实例都正常启用,通过http://localhost:9000/ribbon-consumer
可以获取如下图信息:
关闭一个服务实例
关闭一个节点s43:9999
,当请求轮询到该节点时,请求报错:
当一个节点挂掉之后,并不会被立即从服务清单中剔除,而是在下个
同步
周期时才会被同步,标记为不可用(剔除).
加入熔断器
-
添加maven依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
在应用主类上添加
@EnableCircuitBreaker
注解。@SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public class RibbonHystrixApplication { public static void main(String[] args) { SpringApplication.run(RibbonHystrixApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
或者使用
@SpringCloudApplication
注解:@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public @interface SpringCloudApplication { }
-
Client
改造消费方式。
a. 新增HelloService
,使用@HystrixCommand
指定回调函数。
@Service
public class HelloService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "helloFallback")
public String helloService(){
return restTemplate.getForEntity("http://EUREKA-ECHO-SERVICE/test/hello",String.class).getBody();
}
public String helloFallback(){
return "error";
}
}
b. controller
中直接调用HelloService
来调用服务.
c. 将关闭的服务实例重新启用
,使用http://localhost:9000/ribbon-consumer访问
---- 正常
d.重新关闭服务实例
,当轮询到该实例后,页面请求结果如下图:
除了断开服务实例来模拟某个节点无法访问的情况,还可以通过模拟服务阻塞(长时间未响应)`的情况。
改造服务提供者CLOUD-EUREKA-CLIENT/hello方法:
@RequestMapping("/hello") public String hello() throws InterruptedException { int sleepTime = new Random().nextInt(3000); logger.info("sleep{}ms", sleepTime); Thread.sleep(sleepTime); return "hello world"; }
当服务提供者sleep超过
2000ms
时,服务调用者RIBBON-CONSUMER
会因为调用超时的服务而触发熔断,并调用返回逻辑。