03 为什么选择 Sentinel,Sentinel 与 Hystrix 的对比

为什么选择 Sentinel?

选择 Sentinel 与 Hystrix 通常第一步都是通过对比两者优缺点,Sentinel 与 Hystrix 的对比结果是作为我们选择 Sentinel 还是 Hystrix 的直观参考,不过使用 Sentinel 能做什么,是否满足实际需求才是我们最终决定是否使用 Sentinel 的最关键因素,否则尽管一个框架再好,不适合也不会选择。

以下 Sentinel 与 Hystrix 的对比,表格来自 Sentinel Github 官方文档。

SentinelHystrix
隔离策略信号量隔离线程池隔离/信号量隔离
熔断降级策略基于响应时间或失败比率基于失败比率
实时指标实现滑动窗口滑动窗口(基于 RxJava)
规则配置支持多种数据源支持多种数据源
扩展性多个 SPI 扩展点插件的形式
基于注解的支持支持支持
限流基于 QPS,支持基于调用关系的限流有限的支持
流量整形支持慢启动、匀速器模式不支持
系统负载保护支持不支持
控制台开箱即用,可配置规则、查看秒级监控、机器发现等不完善
常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC 等Servlet、Spring Cloud Netflix

Sentinel 于 2018 年 7 月份开源,截至本文写作之日已有 13k 的 Star,而 Hystrix 已经停止开发。Hystrix 不再维护可能也是 Sentinel 能快速进入大家眼球的原因之一。Hystrix 虽然不再维护,但依然开源,且 Hystrix 已经很稳定,不会因为不维护就不可用,只是不会再有更新。相比 Hystrix,Sentinel 更易于上手,Sentinel 是国内开源的项目,官方有提供中文文档,因此对只会中文的开发者较为友好,并且文档介绍得也很全面。

Hystrix 最核心的一项功能就是资源隔离,支持线程池隔离和信号量隔离。Sentinel 不支持线程池隔离,但使用 Sentinel 也可通过控制并发线程数方式提供信号量隔离。例如,控制服务 A 同时只能有 5 个线程去调用服务 B 的某个接口,或者控制服务 B 同时只能有 5 个线程去处理服务 A 发起调用的某个接口,避免因为一个接口消耗全部线程资源,导致服务奔溃。Hystrix 的线程池隔离可以为每个资源配置单独的线程池用于发送请求,但资源多的情况下会导致线程池增多,线程池增多会导致线程数增多,上下文切换会有非常大的损耗,对低延时的调用影响较大,因此 Hystrix 的线程池隔离也并未体现出多大的优势。

Sentinel 与 Hystrix 都支持基于失败比率的熔断降级,在调用超过指定的数量并且失败比率达到设定的阈值时触发熔断,并在下个时间窗口自动恢复。Sentinel 也支持按失败总数熔断降级,但按失败总数的熔断降级固定时间窗口为 1 分钟,当 1 分钟内调用失败总数达到设定的阈值就会触发熔断。除此之外,Sentinel 还支持基于平均响应时间的熔断降级,平均响应时间越长,说明服务的性能在持续下降,在响应时间持续飙高时自动熔断,可以防止调用慢造成级联阻塞。

Sentinel 和旧版本 Hystrix 的实时指标数据统计实现都是基于滑动窗口,指标数据统计指的是统计每个资源的当前窗口时间内的请求总数、处理成功总数、失败总数、总耗时、平均耗时、最大耗时、最小耗时、被降级总数等。使用滑动窗口可以循环利用一个数组,不需要重新申请内存,也不需要去删除过期的统计数据,降低 GC 的压力。Hystrix 1.5 版本对实时指标统计的实现进行了重构,将指标统计数据结构抽象成响应式流(reactive stream)的形式,方便消费者去利用指标信息,同时底层改造成基于 RxJava 的事件驱动模式,在服务调用成功、失败或超时时发布事件,通过一系列的变换和聚合最终得到实时的指标统计数据流,可以被熔断器或 Dashboard 消费 [1]。Sentinel 官方表示,未来将支持响应式流。

Sentinel 提供数据源接口可实现动态加载规则配置,结合 loadRules API 可灵活的运行时修改规则配置,并且随时修改随时生效。动态修改不仅支持修改某资源的规则配置,也支持添加新的资源规则配置或者移除资源规则配置。Sentinel 支持给同一个资源同时添加多种规则配置,当对同一个资源配置多种规则时,哪个规则先达到阈值就会触发哪个规则的降级。Hystrix 的资源模型设计上采用了命令模式,在创建 Command 时就需要指定隔离策略是线程池隔离还是信号量隔离,一但指定了隔离策略,运行期间便不能修改隔离策略,而在 Sentinel 中资源定义和规则配置是分离的,因此在配置的灵活性上 Sentinel 更具有优势。

Sentinel 支持系统自适应限流,Hystrix 所不支持的。当系统负载较高的时候,如果仍持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,负载均衡把本应这台机器承载的流量转发到其它的机器上去,如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器崩溃,最后导致整个集群不可用。针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求 [2]。

在 QPS 过高的情况下,直接拒绝超出限制的请求是最常见的实现方式,但有些场景我们可能并不想直接拒绝请求,而是让请求排队等待处理,例如某一秒突增请求过多,但下一秒可能又没有请求或者请求很少的情况。Sentinel 的流量控制支持多种模式,例如直接拒绝模式、慢启动预热模式、匀速器模式。而慢启动预热模式和匀速器模式也是 Hystrix 所不支持的。

Sentinel 在框架的设计上使用了责任链模式和 SPI 机制提供扩展功能。使用 SPI 机制为 Sentinel 提供了更好的扩展性,例如,使用 SPI 支持自定义调用链构建器,使用自定义调用链构建器可实现按需选配降级功能(ProcessorSlot),并且还可以自己添加自定义降级功能(ProcessorSlot)。简单说,我们可以实现自定义降级功能,或者根据需要选择 Sentinel 提供的降级功能,移除不需要的,尽量降低 Sentinel 对服务的性能影响。

Sentinel 还支持集群限流。除了轮询负载均衡算法外,其它的算法都会导致流量到集群的每个节点都不一样,有的多有的少,假设服务 B 部署两个节点,对每个节点配置限流阈值为 200 QPS,我们希望集群的整体限量阈值是 400 QPS,但实际情况可能 400 个请求有 300 个请求分配到节点 1,只有 100 个请求分配到节点 2,导致总量没有到的情况下节点 1 就开始限流,因此单机维度限流会无法精确地限制总体流量。

集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。但实现集群限量需要一个 Server 专门统计集群的总调用量,其它的实例都要与 Server 通信来判断是否可以调用,也意味着每个请求都要增加一次额外的与 Server 通信的消耗,不过在使用单一长连接且 Server 处理“判断是否能通过”的耗时非常低,这个消耗对性能的影响还是可以接受的。

另外,黑白名单限流和热点参数限流也是 Sentinel 的一大特色。

黑白名单限流,可根据请求来源判断来源是否在黑名单中,如果在黑名单中则拒绝请求,否则放行,结合 Sentinel 的灵活动态配置,黑白名单可用于高峰期间对某些服务限流。

热点参数限流,统计调用 SphU.entry 传入参数中的热点参数,根据配置的限流阈值对包含热点参数的资源调用进行限流。热点参数限流仅对包含热点参数的资源调用生效,Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法进行参数级别的流控,并且支持匀速流控效果。例如,想对某个热卖的商品限流,可将商品的 ID 为参数,统计一段时间内访问量最多的商品并进行限制。

Sentinel 提供的多种限流功能基本满足我们的所有需求,并且提供切入点让使用者可扩展 Sentinel 的功能,加上灵活的规则配置,以及 Sentinel 在性能上所作出的努力,都是 Sentinel 被广泛使用的原因。Sentinel 有阿里巴巴做信用背书,也在不断的优化、不断更新,相信 Sentinel 能做得更好。

参考文献