Spring Cloud Hystrix 服务容错保护的原理实现
一、hystrix 是什么
在微服务架构中,我们将系统拆分成了若干弱小的单元,单元与单元之间通过http或者tcp等方式相互访问,各单元的应用间通过服务注册与订阅的方式相互依赖。由于每个单元都在不同的进程中运行,依赖 远程调用
的方式执行,这样就可能引起因为网速变慢或者网络故障导致请求变慢或超时,若此时调用方的请求在不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。
hystrix
是netflix 中的一个组件库,它隔离了服务之间的访问点,阻止了故障节点之间可能会引起的雪崩效应,并提供了后备选项。
在微服务架构中,存在着许多的服务单元,若单一节点的故障,就很容易因为依赖关系而引发故障的蔓延,最终导致整个生态系统的瘫痪。为了解决这样的问题,产生了 断路器
等一系列的保护机制措施。
在 分布式架构中
,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
雪崩效应
雪崩效应就像是水滴石穿,蝴蝶效应一样,是指微小的事物随着时间的推移,会变得越来越巨大,从而对整个环境造成影响的现象。例如:在生态系统中,某一类物种的灭绝可能对整个生态系统造成不了太大的损失,但是这类物种的灭绝可能会引发其他物种的死亡,其他物种的灭绝又会影响另外一种物种的灭亡,就像雪球越滚越大,最终会导致整个生态系统的崩溃。
如上图所示:a作为服务提供者,b为a的服务消费者,c和d是b的服务消费者。a不可用引起了b的不可用,并将不可用像滚雪球一样放大到c和d时,雪崩效应就形成了。
雪崩效应产生场景
流量激增
: 比如异常流量,用户重试导致系统负载升高;
缓存刷新
: 假设a为 client 端,b为 server 端,假设a系统请求都流向b系统,请求超出了b系统的承载能力,就会造成b系统崩溃
连接未释放
: 代码循环调用的逻辑问题,资源未释放引起的内存泄漏等问题;
硬件故障
: 比如宕机,机房断电等
线程同步等待
: 系统间经常采用同步服务调用模式,核心服务和非核心服务共用一个线程池和消息队列。如果一个核心业务线程调用非核心业务线程,这个非核心线程交由第三方系统完成,当第三方系统本身出现问题,导致核心线程阻塞,一直处于等待状态,而进程间的调用是有超时限制的,最终这条线程将断掉,也可能引发雪崩;
常见解决方案
针对上述的雪崩问题,每一条都有一个自己的解决方案,但是任何一个解决方案能够应对所有场景
- 针对流量激增,采用自动扩容以应对流量激增,或者在负载均衡器上安装限流模块
- 针对缓存刷新,参考cache应用的服务过载案例研究
- 针对硬件故障,采用多机房灾备,跨机房路由
- 针对同步等待,采用线程隔离,熔断器等机制
通过实践发现,线程同步等待是最常见引发的雪崩效应的场景。
二、hystrix断路器搭建
在开始使用spring cloud hystrix断路器之前,我们先用之前实现的一些内容作为基础,构建一个如下图所示的服务调用关系:
如图所示,上面需要的角色有三个,服务有四个
- ribbon-connsumer: ribbon消费者,消费server-provider提供的服务
- server-provider: 服务提供者,提供服务供消费者消费(有点像父母默默的付出一样),启动两个实例,还记得怎么启动吗?—server.port 启动
- eureka-server: eureka注册中心,提供最基本的订阅发布功能。消费者和服务提供者都需要往注册中心注册自己
依次启动上面的四个服务,发现注册中心已经成功注册了四个服务(包括自己)
调用http://localhost:9000/ribbon-consumer 发现能够通过ribbon进行远端调用
在未加入断路器之前,关闭ribbon-consumer 的连接,再次调用http://localhost:9000/ribbon-consumer,发现服务无法提供(使用postman 测试)
下面开始引入hystrix
在ribbon-consumer 工程的pom.xml的dependency节点下引入spring-cloud-starter-hystrix依赖
在ribbon-consumer 工程的 主加载类
中添加 @enablecircuitbreaker
开启断路器的功能
注意:这里也可以使用@springcloudapplication注解来修饰应用主类,具体定义如下
@target(elementtype.type) @retention(retentionpolicy.runtime) @documented @inherited @springbootapplication @enablediscoveryclient @enablecircuitbreaker public @interface springcloudapplication {}
springcloudapplication 注解上有@enablecircuitbreaker 注解,用来开启断路器的功能,其他主要注解是@springbootapplication ,这个注解是springboot的启动类注解, @enablediscoveryclient该注解可以发现eureka注册中心
改造消费方式,新增 hystrixservice
类,并且注入 resttemplate
实例,然后,将在ribboncontroller中对resttemplate 的使用迁移到hystrixservice方法中,最后,在hystrixservice上添加@hystrixcommand注解来指定回掉方法。
// hystrixservice @service public class hystrixservice { @resource resttemplate resttemplate; // 指定回掉方法是下面的hystrixcallback @hystrixcommand(fallbackmethod = "hystrixcallback") public string hystrixservice(){ return resttemplate.getforentity("http://server-provider/hystrix",string.class).getbody(); } public string hystrixcallback(){ return "error"; } }
服务提供者
的业务非常简单,具体代码如下
@requestmapping(value = "/hystrix", method = requestmethod.get) public string hystrix(){ return "hystrix"; }
下面来验证一下通过断路器的回掉实现,重启之前关闭的8081端口,恢复成为四个服务的状态,并确保http://localhost:9000/ribbon-consumer/ 能够提供服务,并且以轮询的方式循环访问8081 和 8082 端口的服务。此时断开8081端口,发现页面上展示的不再是 hystrix ,而是"error",而另一个服务是正常能够打印。
三、断路器优化
经过以上服务的搭建,相信你已经能够搭建出来最基本的hystrix熔断器,并且实现了服务熔断机制,下面就来对断路器做一下简单的优化,来模拟 服务阻塞(长时间未响应)
的情况。
优化 server-provider
代码如下:
@requestmapping(value = "/hystrix", method = requestmethod.get) public string hystrix() throws interruptedexception { serviceinstance serviceinstance = discoveryclient.getlocalserviceinstance(); // 让线程等待几秒钟 int sleeptime = new random().nextint(3000); thread.sleep(sleeptime); system.out.println("weak up!!!"); log.info("sleeptime = " + sleeptime); return "hystrix"; }
依次启动所有的服务,在主页*问 http://localhost:9000/ribbon-consumer ,多次刷新主页,发现error 和 hystrix 是交替出现的,这是为何?
因为hystrix断路器的 默认超时时间
是2000毫秒,所以这里采用了0 - 3000 的随机数,也就是访问请求在 0 -2000 毫秒内是不超时的,不会触发断路器,而> 2000 毫秒是超市的,默认会触发断路器。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
Spring Cloud Hystrix 服务容错保护的原理实现
-
Spring Cloud Eureka服务治理的实现
-
详解使用Spring Cloud Consul实现服务的注册和发现
-
spring cloud 之 Feign 使用HTTP请求远程服务的实现方法
-
Spring Cloud Eureka服务治理的实现
-
详解使用Spring Cloud Consul实现服务的注册和发现
-
spring cloud 之 Feign 使用HTTP请求远程服务的实现方法
-
详解Spring Cloud Hystrix断路器实现容错和降级
-
spring cloud 使用Hystrix 实现断路器进行服务容错保护的方法
-
详解spring cloud使用Hystrix实现单个方法的fallback