阿里面向分布式服务架构的流量控制组件开源了

作者宿何,阿里巴巴高级开发工程师
Sentinel (https://github.com/alibaba/Sentinel)是阿里巴巴开源的,面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统自适应保护等多个维度来帮助开发者保障微服务的稳定性。Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀、冷启动、消息削峰填谷、集群流量控制、实时熔断下游不可用服务等,是保障微服务高可用的利器,原生支持 Java/Go/C++ 等多种语言,并且提供 Istio/Envoy/SOFA MOSN 全局流控支持来为 Service Mesh 提供高可用防护的能力。

近期,Sentinel Go 0.4.0 (https://github.com/alibaba/sentinel-golang/releases/tag/v0.4.0)正式发布,带来了热点参数流控特性(https://github.com/alibaba/sentinel-golang/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81),可以自动识别统计传入参数中的“热点”参数值并分别进行流控,对于防刷、热点商品访问频次控制等场景非常有用,是高可用流量防护中重要的一环。
下面我们来了解一下热点参数流控的场景和原理。

01

热点流量防护介绍

流量是随机的,不可预测的。为了防止被大流量打垮,我们通常会对核心接口配置限流规则,但有的场景下配置普通的流控规则是不够的。我们来看这样一种场景——大促峰值的时候,总是会有不少“热点”商品,这些热点商品的瞬时访问量非常高。一般情况下,我们可以事先预测一波热点商品,并对这些商品信息进行缓存“预热”,以便在出现大量访问时可以快速返回而不会都打到 DB 上。但每次大促都会涌现出一些“黑马”商品,这些“黑马”商品是我们无法事先预测的,没有被预热。当这些“黑马”商品访问量激增时,大量的请求会击穿缓存,直接打到 DB 层,导致 DB 访问缓慢,挤占正常商品请求的资源池,最后可能会导致系统挂掉。这时候,利用 Sentinel 的热点参数流量控制能力,自动识别热点参数并控制每个热点值的访问 QPS 或并发量,可以有效地防止过“热”的参数访问挤占正常的调用资源。

再比如有的场景下我们希望限制每个用户调用某个 API 的频率,将 API 名称+userId 作为埋点资源名显然是不合适的。这时候我们可以在给 API 埋点的时候通过 WithArgs(xxx) 将 userId 作为参数传入到 API 埋点中,然后配置热点规则即可针对每个用户分别限制调用频率;同时,Sentinel 也支持针对某些具体值单独配置限流值,进行精细化流控。
热点参数埋点/规则示例:

// 埋点示例

e, b := sentinel.Entry("my-api", sentinel.WithArgs(rand.Uint32()%3000, "sentinel", uuid.New().String()))


// 规则示例 _, err = hotspot.LoadRules([]*hotspot.Rule{ { Resource: "my-api", MetricType: hotspot.QPS, // 请求量模式 ControlBehavior: hotspot.Reject, ParamIndex: 0, // 参数索引,0 即为第一个参数 Threshold: 50, // 针对每个热点参数值的阈值 BurstCount: 0, DurationInSec: 1, // 统计窗口时长,这里为 1s SpecificItems: map[hotspot.SpecificValue]int64{ // 支持针对某个具体值单独配置限流值,比如这里针对数值 9 限制请求量=0(不允许通过) {ValKind: hotspot.KindInt, ValStr: "9"}: 0, }, }, })

Sentinel Go 提供的 RPC 框架整合模块(如 Dubbo、gRPC)均会自动将 RPC 调用的参数列表附带在埋点中,用户可以直接针对相应的参数位置配置热点流控规则。目前热点规则仅支持基本类型和字符串类型,后续社区会进一步进行完善,支持更多的类型。
Sentinel Go 的热点流量控制基于缓存淘汰机制+令牌桶机制实现。Sentinel 通过淘汰机制(如 LRU、LFU、ARC 策略等)来识别热点参数,通过令牌桶机制来控制每个热点参数的访问量。目前 0.4.0 版本采用 LRU 策略统计热点参数,在后续的版本中社区会引入更多的缓存淘汰机制来适配不同的场景。

02

高可用流量防护最佳实践

在服务提供方(Service Provider)的场景下,我们需要保护服务提供方不被流量洪峰打垮。我们通常根据服务提供方的服务能力进行流量控制,或针对特定的服务调用方进行限制。为了保护服务提供方不被激增的流量拖垮影响稳定性,我们可以结合前期的容量评估,通过 Sentinel 配置 QPS 模式的流控规则,当每秒的请求量超过设定的阈值时,会自动拒绝多余的请求。同时可以结合热点参数流控进行细粒度的流量防护。
在服务调用端(Service Consumer)的场景下,我们需要保护服务调用方不被不稳定的依赖服务拖垮。借助 Sentinel 的信号量隔离策略(并发数流控规则),限制某个服务调用的并发量,防止大量慢调用挤占正常请求的资源;同时,借助熔断降级规则,当异常比率或业务慢调用比例超过某个阈值后将调用自动熔断,直到一段时间过后再尝试恢复。熔断期间我们可以提供默认的处理逻辑(fallback),熔断期间的调用都会返回 fallback 的结果,而不会再去尝试本已非常不稳定的服务。需要注意的是,即使服务调用方引入了熔断降级机制,我们还是需要在 HTTP 或 RPC 客户端配置请求超时时间,来做一个兜底的保护。


在一些请求突刺的场景中,比如 MQ 客户端消费消息的场景,我们可能不希望将多余的消息直接拒绝(重投),而是让这些过量的消息排队逐步处理。这就是“削峰填谷”的场景。我们可以利用 Sentinel 流控规则中的“匀速+排队等待”控制效果来处理这种场景,以固定的间隔时间让请求通过,超出预设量的请求排队等待。这种方式适合用于请求以突刺状来到,这个时候我们不希望一下子把所有的请求都通过,这样可能会把系统压垮;同时我们也期待系统以稳定的速度,逐步处理这些请求,以起到“削峰填谷”的效果,而不是直接拒绝所有多余的请求。


同时 Sentinel Go 还提供 全局维度的系统自适应保护能力(https://github.com/alibaba/sentinel-golang/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E6%B5%81%E6%8E%A7),结合系统的 Load、CPU 使用率以及服务的入口 QPS、响应时间和并发量等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。系统规则可以作为整个服务的一个兜底防护策略,保障服务不挂。

03

Let’s start hacking!

Sentinel Go 版本正在快速演进中,我们非常欢迎感兴趣的开发者参与贡献,一起来主导未来版本的演进。Sentinel Go 版本的演进离不开社区的贡献。若您有意愿参与贡献,欢迎联系我们加入 Sentinel 贡献小组一起成长(Sentinel 开源讨论钉钉群:30150716)。我们会定期给活跃贡献者寄送小礼品,核心贡献者可以提名为 committer,一起主导社区的演进。同时,也欢迎大家通过 AHAS Sentinel 控制台(点击 阅读原文
) 来快速体验 Sentinel 的能力。Now let’s start hacking!