首先,了解海斯特里克斯

海斯特里克斯是网飞的一个开源容错框架,它包括常见的容错方法:线程池隔离、信号量隔离、融合和降级回滚。在高并发访问下,系统所依赖的服务的稳定性对系统的影响很大,不可控因素很多,比如网络连接慢、资源突然繁忙、暂时不可用、离线服务等。如果我们想构建一个稳定可靠的分布式系统,就必须有这样的容错方法。

本文将逐一分析线程池隔离、信号量隔离、融合和降级回退的原理和实践。

第二,线程隔离

2.1为什么要进行线程隔离

比如我们现在有三个业务调用,订单查询、商品查询、用户查询,这三个业务请求都依赖于第三方服务——订单服务、商品服务、用户服务。这三个服务都是通过RPC调用的。当查询订单服务时,如果线程被阻塞,此时有大量的查询订单请求到来,那么容器中的线程数量将继续增加,直到CPU资源耗尽到100%,整个服务对外不可用,这是集群环境中的雪崩。下图

订单服务不可用。png

整个tomcat容器都不可用。

2.2、线程隔离——线程池

2.2.1.海斯特里克斯如何通过线程池实现线程隔离

海斯特里克斯通过命令模式将每种类型的业务请求封装成相应的命令请求,如查询订单-->:订单命令、查询商品-->:商品命令、查询用户-->:用户命令。每种类型的命令对应一个线程池。创建的线程池被放入并发哈希映射中,例如查询订单:

最终静态ConcurrentHashMap & lt字符串,Hystrixthreadpool >;thread pools = new ConcurrentHashMap & lt;字符串,Hystrixthreadpool >;;

threadPools.put);

当第二个查询顺序请求到来时,可以直接从映射中获取线程池。具体流程如下:

Hystrix线程执行过程和异步。png

关于在线程池中创建线程的方法,请按如下方式检查源代码:

在线程池中创建线程。png

执行命令有四种方式。直接看官方文档。具体区别如下:

Execute:在同步阻塞模式下执行run。在调用execute之后,hystrix首先创建一个新的路由来运行run,然后在调用execute时阻塞调用程序,直到run结束。

Queue : run异步执行,不被阻塞。调用queue直接返回一个Future对象,hystrix创建一个新的路由来运行run。调用者通过Future.get得到run的返回结果,但是Future.get被阻塞。

Observe:在事件注册之前运行/construct。第一步是调用observe在事件注册前自动触发run/construct的执行,而不会阻塞;如果继承是HystrixObservableCommand,则构造将在调用方的线程阻塞的情况下执行。第二步是调用者在从observe返回后调用subscribe来完成事件注册。如果成功执行run/construct,将触发onNext和onCompleted,如果执行异常,将触发。

Toobservable:事件注册后运行/construct。第一步是在事件注册之前,调用toObservable将直接返回一个可观察的

注意:

Execute和queue是HystrixCommand中的方法,observe和toobservablecommand是HystrixObservableCommand中的方法。就底层实现而言,HystrixCommand实际上是由Observable实现的,虽然HystrixCommand只返回一个单一的结果。HystrixCommand的queue方法实际上调用了toobservable。toblocking。tofuture,而execute方法实际调用queue。get。

2.2.2、如何应用于实际代码

使用线程池的实际代码。png

2.2.3.线程隔离-线程池摘要

执行依赖代码的线程与请求线程是分离的,请求线程可以自由控制离开时间。这也是我们通常所说的异步编程,Hystrix是异步编程结合RxJava。通过设置线程池的大小来控制并发访问,当线程饱和时,可以拒绝服务,防止依赖性问题扩散。

线程隔离。png

线程池隔离的优势:

:应用程序将被完全保护,即使一个依赖服务的线程池已满,也不会影响应用程序的其他部分。

:当我们引入一个新的对应用风险较低的客户端lib时,如果有问题也在这个lib中,不会影响其他内容,可以大胆引入一个新的lib库。

:当相关的失败服务恢复正常时,应用程序将立即恢复正常性能。

:如果我们的应用程序的一些参数配置错误,线程池的运行状态会很快显示出来,如延迟、超时、拒绝等。同时,可以通过实时执行动态属性来处理用于纠错的参数配置。

:如果服务的性能发生变化,需要进行调整,如增加或减少超时,改变重试次数,可以通过线程池指示器修改动态属性,而不影响其他调用请求。

:hystrix除了隔离的优点外,还有一个特殊的线程池,可以提供内置的并发功能,使得在同步调用之上构建异步外观模式成为可能,这样异步编程就可以很方便的完成。

虽然线程池提供了线程隔离,但是我们客户端的底层代码必须有一个超时设置,不能无限期阻塞,这样线程池总是饱和的。

线程池隔离的缺点:

:线程池的主要缺点是增加了计算开销。当执行每个服务请求时,它将涉及请求排队、调度和上下文切换。然而,网飞内部认为线程隔离开销足够小,不会造成显著的成本或性能影响。

网飞应用编程接口使用线程隔离每天处理100多亿次海斯特里克斯命令执行。每个应用编程接口实例有40多个线程池,每个线程池有5-20个线程。

网飞应用编程接口使用线程隔离来处理每天10亿次的海斯特里克斯命令执行。每个API实例有40多个线程池,每个线程池有5-20个线程。

对于不依赖网络访问的服务,比如只依赖内存缓存,不适合线程池隔离技术,适合信号量隔离。

2.3.线程隔离-信号量

2.3.1.线程池和信号量的区别

线程池的缺点已经在上面提到过了。当我们所依赖的服务具有极低的延迟时,比如访问内存缓存,就没有必要使用线程池,在这种情况下开销是得不偿失的,但是建议使用信号量。下图说明了线程池隔离和信号量隔离的主要区别:服务请求线程和执行依赖服务的线程在线程池模式下不是同一个线程;在信号量模式下,服务请求线程和执行相关服务的线程是同一个线程

信号量和线程池的区别。png

2.3.2.如何使用信号量隔离线程

将属性execution.isolation.strategy设置为SEMAPHORE,就像这个execution solution strategy . SEMAPHORE一样,然后Hystrix使用semaphores而不是默认的线程池进行隔离。

信号量使用。png

2.3.4.线程隔离-信号量摘要

信号量隔离的方式是限制并发总数。每次请求到来时,请求线程和调用依赖服务的线程都是同一个线程。如果不涉及远程RPC调用,则使用信号量隔离,这样更轻,成本更低。

第三,保险丝

3.1断路器介绍

保险丝,现实生活中有一个很好的类比,就是家里电路会装一个保险丝盒。当电流过大时,保险丝盒内的保险丝会自动熔断,以保护家中的各种电器和电路。海斯特里克斯的断路器也起到这样的作用。在操作期间,海斯特里克斯将向对应于每个命令键的保险丝报告成功、失败、超时和拒绝状态。保险丝维护统计数据,并根据这些统计信息确定保险丝是否断开。如果打开,后续请求将被截断。然后,它会每隔一段时间默认为5s,尝试打开一半,并放入一些流量请求,这相当于对依赖的服务进行健康检查。如果恢复了,保险丝就会断开,然后通话就完全恢复了。如下图:

保险丝开关图。png

说明上面提到的commandKey是初始化时设置的command key)。

从步骤4开始,查看保险丝在整个海斯特流程图中的位置,如下图所示:

海斯特里克斯流程图。png

海斯特里克斯检查断路器的状态。如果断路器的状态为开,海斯特里克斯将不执行相应的指令,而是直接进入失败处理状态。如果断路器的状态为关闭,海斯特里克斯将继续检查线程池、任务队列和信号量

3.2.如何使用断路器

因为海斯特里克斯是一个容错框架,所以我们在使用它的时候,只需要配置一些参数就可以达到融合的目的。但要想取得实实在在的效果,就必须了解这些参数。断路器包括以下六个参数。

1、断路器已启用

默认情况下,保险丝是否启用为真。

2、断路器。强制打开

保险丝被迫断开,并始终保持断开状态。默认值是FLASE。

3、断路器。强制关闭

保险丝被迫关闭,并始终保持关闭。默认值是FLASE。

4、断路器。错误阈值百分比

设置误差百分比,默认值为50%。例如,如果在一段时间内有100个请求,其中55个是超时或异常返回的,则这段时间内的错误百分比为55%,大于默认值50%。在这种情况下,保险丝熔断。

5、断路器。请求容量阈值

默认值为20。这意味着在计算错误阈值百分比的错误百分比之前,至少有20个请求。例如,在一段时间内,所有19个请求都失败了。错误百分比为100%,但保险丝不会断开,因为requestVolumeThreshold的值为20。这个参数很重要。保险丝是否断开必须首先满足这个条件。源代码如下

保险丝断开顺序条件判断。png

6、断路器。休眠窗口毫秒

半开式探头睡眠时间,默认值为5000毫秒。当保险丝接通一段时间后,例如5000毫秒,它将尝试释放一部分流量进行测试,以确定相关服务是否恢复。

测试代码:

保险丝实际使用代码1.png

保险丝实际使用代码2.png

测试结果:

调用次数:1结果:回退:isCircuitBreakerOpen: false

调用次数:2结果:运行:isCircuitBreakerOpen: false

调用次数:3结果:运行:isCircuitBreakerOpen: false

调用次数:4结果:回退:isCircuitBreakerOpen: false

调用次数:5结果:正在运行:iCircuitbreakeropen:false

调用次数:6结果:回退:isCircuitBreakerOpen: false

调用次数:7结果:回退:iCircuitbreakeropen:false

调用次数:8结果:回退:isCircuitBreakerOpen: false

调用次数:9结果:回退:isCircuitBreakerOpen: false

调用次数:10结果:回退:isCircuitBreakerOpen: false

保险丝断开

调用次数:11结果:回退:iCircuitbreakeropen:true

调用次数:12结果:回退:iCircuitbreakeropen:true

调用次数:13结果:回退:iCircuitbreakeropen:true

调用次数:14结果:回退:iCircuitbreakeropen:true

调用次数:15结果:回退:iCircuitbreakeropen:true

调用次数:16结果:回退:isCircuitBreakerOpen: true

调用次数:17结果:回退:iCircuitbreakeropen:true

调用次数:18结果:回退:iCircuitbreakeropen:true

调用次数:19结果:回退:iCircuitbreakeropen:true

呼叫次数:20结果:回退:iCircuitbreakeropen:true

5s后保险丝关闭

调用次数:21结果:正在运行:isCircuitBreakerOpen: false

调用次数:22结果:正在运行:isCircuitBreakerOpen: false

调用次数:23结果:回退:isCircuitBreakerOpen: false

调用次数:24结果:正在运行:isCircuitBreakerOpen: false

调用次数:25结果:正在运行:isCircuitBreakerOpen: false

3.3.HystrixCircuitBreaker.java断路器分析源代码

HystrixCircuitBreaker.java.png

Factory是一个工厂类,它提供了一个HystrixCircuitBreaker的实例

工厂源代码解析。png

HystrixCircuitBreakerImpl是HystrixCircuitBreaker的实现,allowRequest,isOpen和markSuccess在HystrixCircuitBreakerImpl中都有默认实现。

HystrixCircuitBreakerImpl-1.png

HystrixCircuitBreakerImpl-allowSingleTest。png

HystrixCircuitBreakerImpl-isOpen。png

3.4、保险丝总结

默认情况下,每个保险丝维护10个桶,每秒一个桶。每个blucket记录成功、失败、超时和拒绝的状态。默认错误超过50%,10秒内拦截20多个请求。下图显示了HystrixCommand或HystrixObservableCommand如何与HystrixCircuitBreaker及其逻辑和决策流程交互,包括断路器中计数器的行为。

第四,撤退和降级

4.1.使降低

所谓降级是指我们如何处理Hystrix执行非核心链接功能失败的情况,比如我们返回默认值。如果我们想要返回或降级,我们需要实现方法HystrixCommand.getFallback或hystrixobserveblecommand。代码中的hystrixobserveblecommand。

CommandHelloFailure失败-降级。png

4.2.海斯特里克斯的降级和回退模式

海斯特里克斯有以下降级回退模式:

4.2.1.快速失败快速失败

@覆盖

受保护的字符串运行{

if {

引发新的运行时异常;

} else {

返回“成功”;

}

}

如果我们实现了HystrixObservableCommand.java,就用回退方法覆盖resume with

@覆盖

受保护的可观察<。字符串>resumewithallback{

if {

返回可观察到的错误);

} else {

return observable . just;

}

}

4.2.2.失败无声失败

返回空,空映射,空列表

让silent.png失败

@覆盖

受保护的字符串getFallback {

返回null

}

@覆盖

受保护列表<。字符串>getFallback {

return collections . emptylist;

}

@覆盖

受保护的可观察<。字符串>resumewithallback{

return Observable . empty;

}

4.2.3回退:静态返回默认值

退出时,返回到静态嵌入代码中的默认值,这样函数就不会以Fail Silent的形式明确,也就是用户看不到任何函数。相反,它以默认方式显示。

@覆盖

受保护的布尔getFallback {

返回true

}

@覆盖

受保护的可观察<。布尔型>resumewithallback{

return Observable . just;

}

4.2.4回退:存根集合一个值并返回它

当我们的执行返回的结果是一个包含多个字段的对象时,它将以一种存根的方式返回。我们建议在实例化命令时设置一个值。以countryCodeFromGeoLookup为例。countryCodeFromGeoLookup的值是在我们调用它时注册并初始化的。commandwitstubedfallback command = new commandwitstubedfallback;主要代码如下:

CommandWithStubbedFallback.png

4.2.5回退:缓存通过网络使用远程缓存

通过远程缓存。在失败的情况下,再次发起远程请求,但是这个请求是针对诸如redis之类的缓存的。由于远程调用再次启动,命令将被重新封装。此时,应该注意的是,执行回退的线程必须与主线程区分开来,即重命名一个线程池键。

经由Network.png的缓存

经由Network.png的缓存

4.2.6.主服务器+辅助服务器,带备用服务器

这有点类似于我们日常开发中需要启动一个新的函数,但是为了防止新的函数无法启动,我们可以回到旧的代码。我们会做一个切换,比如用zookeeper作为配置切换,可以动态切换到旧的代码函数。然后,海斯特里克斯使用一种配置在两个命令之间切换。

Fallback.png小学+中学

CommandFacadeWithPrimarySecondary-1.png

CommandFacadeWithPrimarySecondary-2.png

CommandFacadeWithPrimarySecondary-3.png

4.3倒退和降职总结

降级处理模式,返回默认值,返回缓存中的值。

但是,也有不适合回滚的情况:

1.写入操作

2.成批处理

3.计算

如果上述情况失败,程序将向调用者返回错误。

摘要

海斯特里克斯为我们提供了一套在线系统容错的技术实践方法。通过在系统中引入Hystrix jar包,我们可以方便地使用线程隔离、融合、回滚等技术。同时还提供了监控页面配置,方便我们管理和查看各个界面的调用情况。海斯特里克斯也像春云一样引入了微服务构建模式,所以我们可以放心地使用海斯特里克斯的线程隔离技术,防止雪崩一样可怕的致命在线故障。

请注明来源并附上链接

http://www.jianshu.com/p/3e11ac385c73

参考文献:

https://github.com/Netflix/Hystrix/wiki

《十亿级流量网站架构核心技术》一书

激动人心的线下活动推荐:人工智能时代来临。中生界科技组织的ArchData技术峰会全国巡展第二站将在帝都举行。所有大咖都会分享AI相关的精彩话题,如何在企业中应用,让我们在9月24日相聚

1.《hystrix 王新栋 | Hystrix技术解析》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《hystrix 王新栋 | Hystrix技术解析》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/shehui/1777270.html