前言

集群中通常一个服务有多个服务提供者。其中部分服务提供者可能由于网络,配置,长时间 fullgc ,线程池满,硬件故障等导致长连接还存活但是程序已经无法正常响应。单机故障剔除功能会将这部分异常的服务提供者进行降级,使得客户端的请求更多地指向健康节点。当异常节点的表现正常后,单机故障剔除功能会对该节点进行恢复,使得客户端请求逐渐将流量分发到该节点。单机故障剔除功能解决了服务故障持续影响业务的问题,避免了雪崩效应。可以减少人工干预需要的较长的响应时间,提高系统可用率。

这种功能叫做自动故障剔除。

而 SOFA 是怎么实现的呢?

如何使用

自动故障剔除的运行机制:

  • 单机故障剔除会统计一个时间窗口内的调用次数和异常次数,并计算每个服务对应ip的异常率和该服务的平均异常率。
  • 当达到ip异常率大于服务平均异常率到一定比例时,会对该服务+ip的维度进行权重降级。
  • 如果该服务+ip维度的权重并没有降为0,那么当该服务+ip维度的调用情况正常时,则会对其进行权重恢复。
  • 整个计算和调控过程异步进行,不会阻塞调用。

根据官方例子,使用方式如下:

FaultToleranceConfig faultToleranceConfig = new FaultToleranceConfig();
// 是否开启调控.
faultToleranceConfig.setRegulationEffective(true);
// 是否进行降级
faultToleranceConfig.setDegradeEffective(true);
// 时间窗口大小
faultToleranceConfig.setTimeWindow(20);
// 每次权重降级的比率
faultToleranceConfig.setWeightDegradeRate(0.5); FaultToleranceConfigManager.putAppConfig("appName", faultToleranceConfig);

如上,该应用会在打开了单机故障剔除开关,每20s的时间窗口进行一次异常情况的计算,如果某个服务+ip的调用维度被判定为故障节点,则会进行将该服务+ip的权重降级为0.5倍。

可以看到,这个功能面向框架用户的 API 就是这个 FaultToleranceConfig 类,即故障容错配置。

用户可以配置某个服务是否开启调控,是否进行降级,实际窗口大小(秒),每次权重降级的比率。

那么,SOFA 是如何实现的呢?

源码分析

首先说明,由于这个功能相比较之前的功能,代码要复杂一些,因此,本次分析主要分析主流程,不会面面俱到。关于详细的代码细节,我们将在后面的源码分析中详细解释。

1. 初始化

SOFA 对于该功能的设计使用了 Modle 的方式,简单来说,就是一个可扩展,可热插拔的中间件。SOFA 的中间件和 RPC 框架都是通过 Modle 的方式来实现的。

如何实现呢?

RPC 初始化的时候,会调用 RpcRuntimeContext 类的静态块,该静态块内部会初始化其他模块,即调用 ModuleFactory.installModules() 方法。该方法会加载配置文件中所有 Module 接口的 SPI 文件。然后循环调用 install 方法,即初始化。

目前的源码中只有一个 Module 的实现类,即 FaultToleranceModule 。故障容错模块。该类的 install 方法 会创建一个订阅者,在事件总线中订阅两个事件:ClientSyncReceiveEvent 和 ClientAsyncReceiveEvent。然后创建一个事件窗口调控器。并初始化该调控器。

TimeWindowRegulator 是故障调控的核心类,内部包含以下属性:

  1. measureCounter 度量计数器,每执行一次度量任务,就加一。
  2. measureScheduler 度量定时任务线程池,使用 RATE 模式,即从任务开始时间开始计算。如果任务的时间超过了间隔时间,间隔时间将失效。这里的间隔时间是 1 秒。
  3. regulationExecutor 计算线程池,即在定时任务线程池中提交计算任务给这个线程池,以实现快速返回。该线程池核心大小为 2.
  4. measureModels 度量模型,一个存放 MeasureModel 对象的 List。
  5. measureStrategy 计算策略(根据度量结果,判断是否需要执行降级或者恢复)
  6. weightDegradeStrategy 降级策略: 调整权重
  7. logDegradeStrategy 降级策略: 只打印日志
  8. recoverStrategy 恢复策略:调整权重
  9. listener 调用统计监听器,当发生事件时,会调用监听器的方法。

属性很多,暂时不详细解释。说主流程。

该类的 intit 方法是初始化这些属性,并注册监听器。注册方式是调用 InvocationStatFactory.addListener(listener) 方法。而这个监听器是该类的内部类 —— TimeWindowRegulatorListener。

好,初始化完毕之后,开始说流程。

当 RPC 框架调用了发送消息的方法,并返回(或者失败)后,就会向事件总线 EventBus 丢一个事件。例如 ClientSyncReceiveEvent 事件,该事件需要包含以下属性:

    private final ConsumerConfig consumerConfig;
private final ProviderInfo providerInfo;
private final SofaRequest request;
private final SofaResponse response;
private final Throwable throwable;

基本包含了此次调用的所有信息。

此时,就会触发订阅者的 onEvent 方法。即 FaultToleranceSubscriber 的 onEvent 方法。该方法会判断,如果用户启用了自动故障剔除功能,则根据 consumerConfig 和 providerInfo 得到一个调用统计器对象,并对调用次数加一。

关键的方法在 onEvent 中调用的 InvocationStatFactory.getInvocationStat(consumerConfig, providerInfo); 该方法会创建一个 InvocationStat 调用统计器对象,放入 Map 中,而对应的 key 则是根据上面两个参数生成的 InvocationStatDimension 统计维度对象。

创建完 InvocationStat 调用统计器对象后,调用所有监听器的 onAddInvocationStat 方法,表示,我添加了一个监听器了,你可以做点什么了。还记得 TimeWindowRegulator 初始化的时候会添加一个监听器吗。就是这里的监听器。

内部类 TimeWindowRegulatorListener 的方法逻辑如下:

调用度量策略对象的 buildMeasureModel 方法,传入调用统计器。返回一个度量模型。然后,将这个模型添加进 List(measureModels 属性) 中。并调用外部类的 startRegulate 方法,开始进行调控。

startRegulate 方法就是启动了定时任务,使用了一个原子 boolean 变量进行状态判断。

定时任务的内容是什么呢?

答:运行 MeasureRunnable 任务。

该任务首先会对度量计数器加一。然后循环 List 中的 MeasureModel 度量模型。并判断该 MeasureModel 是否到达了用户设定的时间窗口(取于用户设置的时间大小)。

如果到达了时间窗口,并调用 measureStrategy 度量策略对象(serviceHorizontal)的 measure 方法,参数则是度量模型,返回一个度量的结果对象 —— MeasureResult。

得到这个对象后,向计算线程池提交一个 RegulationRunnable 任务,该任务内容如下:

拿到刚刚传入的度量结果拿到度量结果详情的集合 —— measureResultDetails,循环这些集合,并执行 doRegulate 方法,进行调控。

该方法就是真正的对服务进行调控的方法了。首先,一个服务有 3 个状态:健康,异常,忽略。状态来自于刚刚的 measure 方法。

如果用户设置了可以降级的话,则判断服务的度量状态,如果异常了且不超过一个服务的最大调控 IP 数,则执行权重降级逻辑。反之,打印日志。

如果度量状态是正常的,则执行权重恢复,并从降级 IP 列表中删除。

如果用户没有设置可以降级,且度量状态是异常,那么,执行日志降级。即对异常 IP 记性异常信息的日志打印。

当对权重进行降级之后,能够被负载均衡击中的几率就会对应的小很多。甚至了无法击中。

以上,就是 SOFA 自动故障剔除功能的基本实现流程。

总结

还是那句话,由于这个功能比较繁杂,限于篇幅,今天看的是总流程,总结一下这个流程。

RPC 框架在启动的时候,会自动加载故障容错模块,并监听客户端发送事件。同时会初始化故障容错相关的类和监听器。

当发生订阅事件的时候,会调用 onEvent 方法,进而调用 TimeWindowRegulatorListener 的监听器方法。该方法会将度量模型添加进 List 中。

定时任务每隔一秒会调度 MeasureRunnable 任务,内容是根据用户设置的时间窗口处理 List 中的调度模型。

定时任务会向计算任务线程池提交一个 RegulationRunnable 任务。用于处理度量结果中的数据。该任务会循环度量结果的所有度量结果详情,并调用 doRegulate 方法进行调控。

最后,doRegulate 方法则是根据 度量结果详情 的状态判断是否应该对服务 + IP 进行权重降级或者权重恢复,再或者是打印日志 —— 这依据用户设置。

以上就是 SOFA 自动故障剔除的基本流程。后面我们会详细分析自动故障剔除的细节代码。敬请期待。

SOFA 源码分析 — 自动故障剔除的更多相关文章

  1. SOFA 源码分析 —— 服务引用过程

    前言 在前面的 SOFA 源码分析 -- 服务发布过程 文章中,我们分析了 SOFA 的服务发布过程,一个完整的 RPC 除了发布服务,当然还需要引用服务. So,今天就一起来看看 SOFA 是如何引 ...

  2. SOFA 源码分析 —— 服务发布过程

    前言 SOFA 包含了 RPC 框架,底层通信框架是 bolt ,基于 Netty 4,今天将通过 SOFA-RPC 源码中的例子,看看他是如何发布一个服务的. 示例代码 下面的代码在 com.ali ...

  3. SOFA 源码分析 — 调用方式

    前言 SOFARPC 提供了多种调用方式满足不同的场景. 例如,同步阻塞调用:异步 future 调用,Callback 回调调用,Oneway 调用. 每种调用模式都有对应的场景.类似于单进程中的调 ...

  4. SOFA 源码分析 — 负载均衡和一致性 Hash

    前言 SOFA 内置负载均衡,支持 5 种负载均衡算法,随机(默认算法),本地优先,轮询算法,一致性 hash,按权重负载轮询(不推荐,已被标注废弃). 一起看看他们的实现(重点还是一致性 hash) ...

  5. SOFA 源码分析 — 预热权重

    前言 SOFA-RPC 支持根据权重对服务进行预热功能,具体地址:预热权重. 引用官方文档: 预热权重功能让客户端机器能够根据服务端的相应权重进行流量的分发.该功能也常被用于集群内少数机器的启动场景. ...

  6. SOFA 源码分析— 事件总线

    前言 大部分框架都是事件订阅功能,即观察者模式,或者叫事件机制.通过订阅某个事件,当触发事件时,回调某个方法.该功能非常的好用,而 SOFA 内部也设计了这个功能,并且内部大量使用了该功能.来看看是如 ...

  7. SOFA 源码分析 — 自定义线程池原理

    前言 在 SOFA-RPC 的官方介绍里,介绍了自定义线程池,可以为指定服务设置一个独立的业务线程池,和 SOFARPC 自身的业务线程池是隔离的.多个服务可以共用一个独立的线程池. API使用方式如 ...

  8. SOFA 源码分析 — 链路数据透传

    前言 SOFA-RPC 支持数据链路透传功能,官方解释: 链路数据透传功能支持应用向调用上下文中存放数据,达到整个链路上的应用都可以操作该数据. 使用方式如下,可分别向链路的 request 和 re ...

  9. SOFA 源码分析 —— 过滤器设计

    前言 通常 Web 服务器在处理请求时,都会使用过滤器模式,无论是 Tomcat ,还是 Netty,过滤器的好处是能够将处理的流程进行分离和解耦,比如一个 Http 请求进入服务器,可能需要解析 h ...

随机推荐

  1. MySQL最佳实践

    一.核心军规         - 不在数据库做运算:cpu计算务必移至业务层         - 控制单表数据量:单表记录控制在1000w         - 控制列数量:字段数控制在20以内     ...

  2. python的sys模块

    Sys模块函数之多,我只能选取自己认为比较实用的一些函数列在此处.借马云找员工的说法,"找最合适的而不是最天才的",这句话,我个人觉得在很多方面都能适应,学习也不在话下.Sys模块 ...

  3. 9.5、Libgdx加速度计

    (官网:www.libgdx.cn) 加速度计可以让设备通过三个坐标轴检测加速度.通过加速度可以检测设备的方向. 加速度的单位是米每秒的平方.如果一个坐标轴指向地心,加速度大概是-10米每秒的平方.如 ...

  4. Ubuntu14.04安装Matlab2013a

    source url: http://blog.sina.com.cn/s/blog_ec5021d60102v3ky.html 1. 为方便操作,把Matlab镜像文件(iso)重命名为'Matla ...

  5. LeetCode之“链表”:Intersection of Two Linked Lists

    此题扩展:链表有环,如何判断相交? 参考资料: 编程判断两个链表是否相交 面试精选:链表问题集锦 题目链接 题目要求: Write a program to find the node at whic ...

  6. Android性能优化典例(一)

    在Android开发过程中,很多时候往往因为代码的不规范.api使用不恰当.控件的使用场景考虑不全面和用户不恰当的操作等都能引发一系列性能问题的,下面就是我目前整理的一些Android开发过程中需要注 ...

  7. 虚拟机linux挂载光盘显示:mount: you must specify the filesystem type

    虚拟机内 linux 挂载光盘显示:mount: you must specify the filesystem type 今天在虚拟机上挂载镜像文件时提示: 初步断定原因有2: 1.在卸载光盘时使用 ...

  8. 【Linux 操作系统】vim编辑器配置及常用命令

    最近工作不安分, 没有了刚入行时候的锐气, 不知道什么时候开始懈怠起来, 周末在电脑旁边看新闻, 搞笑图片, 追美剧, 一坐就是一天, 很是空虚. 我需要摆脱这种状态, 正好想学习一下安卓底层, An ...

  9. LeetCode之“数学”:Happy Number

    题目链接 题目要求: Write an algorithm to determine if a number is "happy". A happy number is a num ...

  10. android bitmap的内存分配和优化

    首先Bitmap在Android虚拟机中的内存分配,在Google的网站上给出了下面的一段话 大致的意思也就是说,在Android3.0之前,Bitmap的内存分配分为两部分,一部分是分配在Dalvi ...