从不可形容的服务雪崩到初探Hystrix

作者 : 开心源码 本文共4433个字,预计阅读时间需要12分钟 发布时间: 2022-05-12 共172人阅读

什么是服务雪崩?什么是服务保护?服务保护的措施有哪些?熔断怎样做?限流怎样做?服务隔离怎样做?降级怎样做?

真实案例

举一个自己遇到的真实的例子。
接口A依赖了服务B,接口A的部署情况是有两个机房部署,服务B的部署情况也是两个机房部署。客户请求接口失败会重试,部署架构图如下:

ServiceStructure.png

说明:服务部署用到的是Linux+Nginx+PHP技术栈。

当时遇到的情况是服务B所在的机房挂了,导致接口A调用服务B超时,超时返回之后nginx重试到A的另一个节点,继续调用服务B,A的所有节点都失败后,返回给用户端失败结果,用户端进行重试,于是再进行一次刚刚的步骤,这些超时的请求占用了PHP的进程没有释放,同时客户侧体验感知到缓慢,于是不断刷新重试,导致流量暴涨,PHP的进程池被耗尽了,于是接口A就无法访问了,其余依赖接口A的功能也无法使用,导致整个站点雪崩。

这是典型的服务没有进行隔离导致功能雪崩的例子,那么问题来了,假如要对这次的故障进行改进,为接口和服务之间加入一层服务保护,那么要怎样做呢?

业界比较常见的服务保护主要有以下这些:

1、限流

当发现服务失败数量达到某个阈值,拒绝访问,限制更多流量的到来,防止过多失败的请求将资源耗尽。

2、服务隔离

将不同类型的接口隔离部署,单个类型接口的失败甚至进程池被耗尽不会影响其余接口的正常访问,比方在资讯平台中,假如发布和阅读的接口分开部署了,那么即便发布功能失效,阅读功能还能继续使用。

3、熔断

从接口请求连接就拒绝访问,相似家里用的保险丝,使用的电器总和超过了电压就熔断保险丝,整个电路短了,保护整个区域的电路防止更多的损失。

4、降级

对于简单的展现功能,假如有失败的请求,返回默认值。对于整个站点或者用户端,假如服务器负载过高,将其余非核心业务中止,以让出更多资源给其余服务使用。

以上是笔者所知道服务出现雪崩的情况以及保护服务的措施,在Java领域中,业界用得比较多的是Hystrix,那么就来看看它是怎样实现上面这些措施。

Hystrix是什么?

Hystrix是一个通过添加推迟容错和容错逻辑来控制分布式服务之间交互的一个库。Hystrix通过线程隔离,防止错误级联传递,导致服务雪崩,从而提高服务稳固性。

Hystrix的主要目标

1、通过隔离第三方用户端库访问依赖关系,防止和控制推迟和故障;

2、防止复杂分布式系统的级联失败;

3、快速响应失败并迅速恢复;

4、提供回滚以及友好降级;

5、实现近实时监控,告警和操作控制

Hystrix设计准则

1、防止单个依赖耗尽了服务容器的客户线程

2、降低负载以及快速失败,而不是排队

3、当可以阻止服务的失败时提供回退策略

4、使用隔离技术减少任意依赖的影响

5、通过近实时指标、监控和告警优化发现时间

6、在Hystrix的大多数方面,通过配置更改的低推迟和对动态属性更改的支持,使得可以在低推迟的情况下进行实时修改操作,从而优化恢复时间

7、防止整个依赖关系用户端执行中的故障,而不仅仅是网络流量

Hystrix如何做到上面的目标

1、所有外部的调用都封装到HystrixCommand或者HystrixObservableCommand对象,这些对象通常在单独的线程下执行。

2、超时调用的时间,超过定义的阈值。有一个默认值,但是对于大多数的依赖,你可以自己设置该属性使得略高于每个依赖测量的99.5%的性能。

3、为每一个依赖项维护一个线程池(或者者信号),假如依赖项的线程池满了,新的依赖请求不会继续排队等待,而是马上被拒绝访问。

4、计算成功、失败、超时和线程拒绝的数量。

5、假如依赖服务的失败百分比超过阈值,则手动或者自动启动断路器,在一段时间内中止对指定服务的所有请求。

6、为请求失败、被拒绝、超时或者短路情况提供回退逻辑。

7、近乎实时地监控指标和配置更改。

一段代码demo

讲完这么多,还是看看代码更实在,从Hystrix官网上截取了一段代码如下:

public class Order {    private final int orderId;    private UserAccount user;    public Order(int orderId) {        this.orderId = orderId;        user = new GetUserAccountCommand(new HttpCookie("mockKey", "mockValueFromHttpRequest")).execute();    }}

更多代码内容: Netflix/Hystrix/tree/master/hystrix-examples/src/main/java/com/netflix/hystrix/examples/demo

上面就是Hystrix使用的实例,在实际代码中,就是new一个Command,而后调用execute方法获取结果,那么这一个过程中Hystrix做了什么呢?

Hystrix的工作流程

HystrixWorkFlow.png

上面这个图是从Hystrix的官方文档中找到的,能看懂这个文档几乎就能看懂Hystrix是怎样执行的了。通过图中的顺序来解读Hystrix的执行流程。

1、初始化,有两种方式初始化一个Hystrix命令,通过new HystrixCommand或者者new HystrixObservableCommand创立,使用服务实例和请求服务需要的参数来构造一个Hystrix命令。

2、成功创立Hystrix后,有四种方法执行实际的命令并得到返回结果。这里Hystrix还使用了响应式编程来设计,这个主题比较大,一时半会解释不出,之后再深入探究。

对于使用HystrixCommand创立命令的实例,执行execute或者者queue;而对于使用HystrixObservableCommand创立命令的实例,执行observe或者者toObservable方法,可以请求服务而后得到执行结果。这四个方法的特性是:

execute - 会阻塞,而后返回依赖服务的结果queue - 返回一个Future,而后可以通过get方法取得依赖服务的结果。observe - 订阅包含依赖服务响应结果的订阅器,当有结果时返回一个订阅器。toObservable - 返回一个订阅器,当订阅它时,会知晓Hystrix命令并返回结果。

execute的源码如下:

public R execute() {    try {        return queue().get();    } catch (Exception e) {        throw Exceptions.sneakyThrow(decomposeException(e));    }}public Future<R> queue() {    /*     * The Future returned by Observable.toBlocking().toFuture() does not implement the     * interruption of the execution thread when the "mayInterrupt" flag of Future.cancel(boolean) is set to true;     * thus, to comply with the contract of Future, we must wrap around it.     */    final Future<R> delegate = toObservable().toBlocking().toFuture();    // 其余定义}

从源码看到,execute方法会调用queue().get()方法,queue()会调用toObservable().toBlocking().toFuture(),说明每一个Hystrix命令最终都回到Observable对象的实现,即便是为了返回一个简单的值。

3、判断Hystrix能否启用缓存且对应请求有缓存值,则返回缓存的结果。

4、假如3没有缓存,Hystrix会检查它的熔断器,假如此时熔断器开启了,那么Hystrix不会执行命令,直接返回降级结果。

5、假如信号或者者线程池拒绝请求,返回降级结果。

6、Hystrix通过调用HystrixCommand.run()或者者HystrixObservableCommand.construct()方法来触发调用外部服务的操作,假如超时或者者失败,返回降级结果。
假如run或者者construct方法超过了命令定义的超时值,线程会抛出TimeoutException,此时Hystrix捕捉到异常,就会忽略run或者construct方法的返回值,进入fallback。

注意:没有任何方式可以阻止推迟的线程中止工作,在JVM中,Hystrix可以做到最好的就是抛出一个InterruptedException,假如Hystrix封装的服务没有捕获InterruptedException,Hystrix线程池中的线程会继续它的工作。

7、不论请求如何进行:成功、失败、超时、熔断,Hystrix都会上报健康状态到熔断器,记录服务状态,用于判断能否启动/半启动熔断器。

8、fallback,进行降级操作,会触发回退操作的条件:
construct或者者run方法抛出异常
熔断器开启
线程池以及队列或者者信号容量不足
Hystrix命令超时

对于每一个Hystrix命令,都需要覆盖getFallback方法,在fallback函数中实现降级的方案,假如需要在fallback中使用网络调用,那么需要通过另一个HystrixCommand或者者HystrixObservableCommand。在HystrixCommand中是实现getFallback方法,在HystrixObservableCommand中,是实现sumeWithFallback方法。

假如没有实现fallback方法,或者者fallback方法抛出了异常,Hystrix还是会返回一个Observerable,但是不会返回内容并通过一个onError通知来马上终止。通过onError通知,发生异常的会被返回Hystrix的调用者。尽量不要写出可能会抛出异常的fallback实现。

9、假如一切正常,那么Hystrix会发送成功的结果到Observable,程序再去获取。

总结

以上就是Hystrix的执行流程,由于最近想理解在PHP中如何实现服务熔断,于是在学习Java中做的比较好的Hystrix是怎样实现的。接下来会继续深入学习Hystrix的熔断器实现,下次再分享Hystrix熔断器的实现原理。

理解一个库的执行流程,除了有助于开发时排查遇到的较辣手的问题,还可以学习一个库的设计理念,从这些库中吸收少量框架设计优点,之后假如需要实现相关功能时,即可以作为参考。

原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

假如本文对你有帮助,请点个赞吧,谢谢_

更多精彩内容,请关注个人公众号。

qrcode.jpg

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 从不可形容的服务雪崩到初探Hystrix

发表回复