一. 背景
我们的业务当中会大量依赖于第三方的服务,而这些第三方的服务都是基于Http请求的。我们的调用模式基本如下:
因此,第三方资源的稳定性会直接影响我们的业务。除此之外,如果是第三方资源响应速度变慢,则会长时间占用我们的线程池,严重的情况下会产生雪崩效应。
为了防止这种现象发生,我们需要对服务做隔离,熔断,降级以及限流操作。
二. 业务调用示例
2.1 Controller层
2.2 Service业务层
在service当中使用restTemplate操作第三方资源,在这里我使用restTemplate调用睡眠地址,模拟了一下第三方资源的处理过程。
这里的restTemplate.getForEntity("http://127.0.0.1:8080/hystrix/sleep",String.class)就是在模拟第三方http资源的处理,我们这里是处理10秒。
2.3 客户端调用
直接使用浏览器访问http://127.0.0.1:8080/hystrix/hystrixTest或是模拟客户端请求
以上就是一次正常的服务请求,耗时10S。正是我们模拟的第三方资源处理时间。
三. 模拟在第三方资源处理时间内打爆RestTemplate线程池
3.1 我们将服务端RestTemplate连接池设置为5个
3.2 将客户端请求封装的线程中模拟客户端请求
3.3 模拟5个客户端并发访问
3.4 模拟6个客户端并发访问,此时我们的并发访问数量超过了线程池设置的5.为了显示,我给线程加了个name 编号
通过服务端的日志可以看出,服务端正常接收了来自客户端的6个请求。
但是当调用restTemplate. getForEntity()的时候,由于RestTemplate连接池资源只有5个,所以应该有一个是拿不到线程资源的。
从服务端日志可以看出只有5个完成的调用http请求,其中有一个因为从线程池中拿不到连接而抛出异常。
这里我们为了演示方便所以6个客户端请求访问的都是同一个资源,在实际生产环境中,6个客户端请求完全可以是访问不同的第三方资源,然后这样就会存在一个问题,当某一个第三方资源发生延时的,后续访问该资源的请求又在不断的请求访问中,导致该资源拿到线程池资源后不能马上释放,但此时其它的第三方服务却是正常的,但是这里线程池中的所有资源已被占用,所以其它的正常访问也因拿不到线程池资源而无法正常访问。这就是我们之前说的雪崩效应。即某一个依赖服务的问题导致整个业务不可用。
四.Hystrix的引用
我们在项目中引用了Hystrix,Hystrix是用于分布式场景下服务熔断、降级的开源Java库,它的主要作用有,依赖隔离,熔断,降级和监控报警。这里不会过多的介绍Hystrix的概念及定义,具体内容可以看其github网站或是自行搜索,会有大量的仔细介绍。由于Hystrix的原生方式是命令式编程,对原有的业务修改比较大,所以我们这使用的是Hystrix提供的注解方式(hystrix-javanica)
4.1引用hystrix相关依赖
4.2.Spring中增加Aspect
因为使用的是注解方式,所以我们需要设置Aspect
4.3.修改web.xml
五.Hystrix的使用
5.1 业务层增加 @HystrixCommand注解
这里需要指定超时或是发生异常时执行的方法(executeFallBack),这里需要注意的是,fallbackMethod方法的签名要于业务方法签名一致,否则不会生效。
使用execution.isolation.thread.timeoutInMilliseconds设置超时时间,业务执行时间超过这个时间则执行fallbackMethod方法并返回客户端。
5.2 客户端调用
由于业务执行时间为10S,而我们在Hystrix中设置的为5S,所以会触发服务降级,客户端应该返回fallbackMethod方法的结果
可以看出客户端接收到的是fallbackMethod方法返回来的结果。但是细心点的人可以看出来,虽然客户端返回以及服务端执行了fallbackMethod方法,但是原有的业务逻辑仍然继续处理完成了。
因此对于超时的请求,业务不会被中断。当超时引发熔断器启动后,后续一段时间内的请求都将进入fallback,不会再调用业务。
六. 使用Hystrix进行线程隔离
在上面演示中,我们知道业务在超时的时候会运行fallbackMethod,并将fallbackMethod结果返回给客户端,但是业务并没有发生中断,如果这样的话,大量的客户端进行访问的时候,虽然得到了fallbackMethod返回的结果,但是仍然会点用RestTemplate线程池中的资源,还是会出现雪崩的现象。
使用Hystrix定义线程池,启到线程隔离的效果。如下图中,定义Hystrix在调用业务时新建线程池的大小为2.为了测试线程隔离的效果,所以让Hystrix的超时间修改为15秒,大于业务处理的时间10S。
新建一个方法来模拟其它的第三方资源,为了方便我们称之前的资源为A资源,新建的这个资源为B资源(hystrixTest2)
6.1 模拟4个客户端访问系统资源,其中3个访问A资源一个访问B资源。修改了一个客户端线程,让其访问不同的资源
从客户端日志可以看出,访问A资源的3个客户端,有2个成功,1个返回fallback结果,这是因为我们设置的Hystrix的线程池大小为2,所以第三个请求来的时候获取不到可用的线程池资源。而总的restTemplate线程池设置的为5,所以B资源可以继续访问。
再看一下服务端的日志,对B资源访问一次,对A资源访问仅有2次,另一次对A资源的访问因为没有线程资源直接返回fallBack内容。
通过这种方式,可以起到线程隔离的效果。
七.通过Hystrix做降级操作
能过上面的演示可知,当业务超时或是无线程池资源时,会调用Hystrix的fallBack方式,因为我们可以在fallBack中处理服务降级逻辑,如
1.启动限流
2.页面降级
3.读写降级
4.触发开关降级
5.触发熔断器
八.Hystrix熔断器
Hystrix本身就提供熔断功能。
服务的健康状况=请求失败数/请求总数.
熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值决定的。
每个熔断器默认维护10个bucket,每秒一个bucket,每个bucket记录成功,失败,超时,拒绝的状态,默认为错误超过50%且10秒内超过20个请求进行中断拦截.
也就是说在统计窗口10秒内有至少有2个请求,失败率为50%,即一个失败就发生熔断。
客户端每6秒发起一个请求
因此,当熔断打开后请求不会发送的业务方法,而是执行快速失败,这样也启到了保护资源的作用。
九.Hystrix DashBoard
从git下直接下载Hystrix-dashboard,通过gradlew执行
gitclone https://github.com/Netflix/Hystrix.git
cd Hystrix/hystrix-dashboard
../gradlewappRun
9.1 添加Stream.
还记得最早在项目web.xml添加的HystrixMetricsStreamServlet吗,就是在此做监控作用的。
重新运行一下上面发生熔断的例子,然后再看一下我们的dashboard有什么变化
十. Hystrix 并发控制
Hystrix还可以对请求的并发量进行控制。
客户端模拟4个并发请求,只有3个是正常返回业务结果,另一个超过了并发数量限量触发了fallback方法��
服务端业务也只是触发了3次,另一次的请求根本就没有请求的业务逻辑
十一. 参考内容