简介
在微服务架构中,存在着多个微服务,彼此之间可能存在依赖关系,当某个单元出现故障或者网络不通畅时,就会因为依赖关系形成故障蔓延,最终导致整个系统瘫痪,相对于传统架构更加不稳定.为了解决这样的问题,产生了熔断器模式.
熔断器的作用
当某个微服务发生故障时,通过熔断器的故障监控,向调用方返回一个错误响应,而不是长时间的等待,这样就不会使线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中蔓延,造成大面积雪崩效应.
Hystrix 熔断器工作原理
服务端的服务降级逻辑会因 Hystrix命令调用依赖服务超时而触发,也就是调用服务超时会进入断路回调逻辑处理.
但熔断器发挥作用需要满足熔断器三个重要条件:
1.快照时间窗
熔断器确定是否打开需要统计一些请求和错误数据,统计的时间范围就是快照时间窗,默认最近10秒.
2.请求总数下限
在快照时间窗内,必须满足请求总数下限才有资格熔断.默认为20次,如果说10秒内调用次数不足20次,即是请求服务超时,断路也不会打开.
3.错误百分比下限
当请求总数满足要求,但是错误率没有超过下限也不会熔断.默认50%.
因此,熔断器打开的条件是:在10秒快照时间窗期内,至少调用20次服务,并且服务调用错误率超过50%,才会打开熔断器.
不满足以上条件熔断器不会打开,服务调用错误只会触发服务降级,也就是调用fallback函数.每个请求时间延迟就是近似hystrix的超时时间.如果请求时间超过设定超时时间后就会返回fallback.当满足上面条件断路器打开,之后再请求调用的时候将不再调用处理逻辑,而是直接调用降级逻辑,会直接返回fallback,不在等待是否超时.通过断路器实现自动发现错误并将降级逻辑切换为主逻辑,减少响应延迟的效果.
在熔断器打开后,处理逻辑并没有结束.当熔断器打开,对处理逻辑进行熔断之后,hystrix会给处理逻辑设置一个休眠时间窗,在这个时间窗内,降级逻辑为临时的主逻辑,当休眠时间窗结束,熔断器会释放一次请求到处理逻辑,如果此次请求正常返回,那么熔断器闭合,处理逻辑回复正常工作;如果此次请求依然有温蒂,熔断器重新设置一个休眠时间窗.
通过以上机制,hystrix 熔断器实现了对依赖资源故障的处理,对降级策略的主动切换及对处理逻辑的主动恢复.这使得我们的微服务在依赖外部服务或资源的时候得到了非常好的保护,同时对于一些具备降级逻辑的业务需求可以实现自动化的切换和恢复,相比于设置开关由监控和运维来进行切换的传统实现方式显得更为智能和高效.
代码实现
实现原理图
准备工作
继续上一章笔记的工程,启动eureka-server 工程;启动service-hello工程;
创建Hystrix 熔断器
改造ribbon-server工程代码, 在其pom文件中加入hystrix的maven依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在程序的启动类ServiceRibbonApplication 加@EnableHystrix注解开启Hystrix:
@EnableDiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@EnableHystrix
public class ServiceRibbonApplication {
public static void main(String[] args) {
SpringApplication.run( ServiceRibbonApplication.class, args );
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
改造HelloService类,在helloService方法上加上@HystrixCommand注解。该注解对该方法创建了熔断器的功能,并指定了fallbackMethod熔断方法,熔断方法直接返回了一个字符串;
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "helloError")
public String helloService() {
return restTemplate.getForObject("http://SERVICE-HELLO/hello",String.class);
}
public String helloError() {
return "sorry,I'm error!";
}
}
启动:service-ribbon 工程,当我们访问http://localhost:8084/hello 浏览器显示:
hello ! 我是 8083号服务器.
此时关闭 service-hello 工程,来模仿服务故障,再访问http://localhost:8084/hello 浏览器显示:
sorry,I'm error!
这就说明当 service-hello 服务不可用的时候,service-ribbon调用 service-hello的API接口,会执行快速失败,直接返回异常方法,而不是等待响应超时,这很好的控制了容器的线程阻塞。