RPC服务熔断、限流、降级等容灾技术

本篇博客主要包括:限流方式、微服务容灾技术选型、Sentinel的使用、Sentinel动态规则持久化Zookeeper 等。

Sentinel动态规则持久化到Zookeeper的代码实现我已经共享到本人的github,有需要的可以下载使用。

github地址:https://github.com/Force-King/sentinel-dashboard-zk

持久化zk的sentinel-dashboard项目下载地址:https://github.com/Force-King/sentinel-dashboard-zk.git

 

一、限流方式

服务端限流

如果在服务端做限流,无论有多少个客户端,总的提供能力是固定的,所以不会因为客户端数量过多而导致资源不足,因为处理不过来的请求会被阻塞等待获取资源。

缺点

缺点也比较明显,由于服务提供者整体设置了最大限流数,此时所有的客户端共享同一份限流数据,那么有可能导致有的服务能分配到资源有些服务请求分配不到资源导致无法请求的情况。

 

客户端限流

客户端限流解决上服务端限流提到的问题,它能保证每个客户端都能得到响应。但是从其它方面考虑,必须针对不同的客户端做不同的限流策略:

  • 请求量大,但时效性不高,此时将限流数控制小一些会比较合适
  • 请求量大,但时效性高,此时将限流数适当调高
  • 响应时间长,即慢接口,适当降低
  • 主流业务,核心业务,适当调高
  • 非主流业务,适当降低

缺点

  • 如果客户端的数量不固定,那么有可能导致客户端数量过多造成大量请求打到服务端导致处理不了的结果,所以需要严格监控客户端的调用情况。
  • 配置复杂,需要针对每个客户端做相对精准的判断

 

二、选型

1. 目前开源容灾框架有Hystrix、Sentinel、Resilience4j

 

Hystrix:常用于Spring Cloud的一个熔断降级组件。主要功能是不同服务之间的资源隔离、失败降级。底层实现是Rxjava。它提供两种资源隔离的模式:信号量隔离和线程池隔离。一般使用线程池隔离。耗费一定资源,但相比之下支持超时和异步执行。听起来可以覆盖大部分场景,但它不支持更高要求的流控,如qps的控制。所以需要单独采用令牌漏桶来做流量控制。

Sentinel:阿里开源的分布式流量控制组件。支持流控、熔断降级、系统保护等。所有的资源都对应一个资源名称以及一个Entry。每一个Entry创建的时候,同时也会创建一系列插件(系统保护插件:SystemSlot、流控插件:FlowSlot、熔断降级插件LDegradeSlot等)。每个插件会监控自己职责范围内的指标。NodeSelectorSlot将各个资源的调用路径以树状存储,用于限流降级。调用者通过创建上下文、请求token来执行方法。若没有抛出BlockException,表示请求成功。它支持并发数/qps的流量控制、也支持熔断降级。

Resilience4j:一个比较轻量的熔断降级库。模块化做的比较好,将每个功能点(如熔断、限速器、自动重试)都拆成了单独的模块,这样整体结构很清晰,用户也只需要引入相应功能的依赖即可;另外resilience4j 是针对 Java 8 和函数式编程设计的,API 比较简洁优雅。同时与 Hystrix 相比,Resilience4j 增加了简单的限速器和自动重试特性,使用场景更加丰富。Resilience4j 属于一个新兴项目,社区也在蓬勃发展。总的来说,Resilience4j 是比较轻量的库,在较小较新的项目中使用还是比较方便的,但是 Resilience4j 只包含限流降级的基本场景,对于非常复杂的企业级服务架构可能无法很好地 cover 住;同时 Resilience4j 缺乏生产级别的配套设施(如提供规则管理和实时监控能力的控制台)。

2. Sentinel 的优势和特性

  • 轻量级,核心库无多余依赖,性能损耗小。
  • 方便接入,开源生态广泛。

              Sentinel 对 Dubbo、Spring Cloud、Web Servlet、gRPC 等常用框架提供适配模块,只需引入相应依赖并简单配置即可快  速接入;同时针对自定 义的场景 Sentinel 还提供低侵入性的注解资源定义方式,方便自定义接入。

  • 丰富的流量控制场景。

              Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,流控维度包括流控指标、流控效果(塑形)、调用关系、热点、集群等各种维度,针对系统维度也提供自适应的保护机制。

  • 易用的控制台,提供实时监控、机器发现、规则管理等能力。
  • 完善的扩展性设计,提供多样化的 SPI 接口,方便用户根据需求给 Sentinel 添加自定义的逻辑。

 

Sentinel 的主要特性

<think>嗯,用户想设计一个支持百万级QPS的分布式限流系统,这确实是个挑战。首先得理解高QPS下的系统压力,传统的单点限流可能无法应对,所以需要考虑分布式方案。用户提到的引用里提到了Redis+Lua、分片式限流,还有Redisson的实现,这些可能需要结合起来。 首先,系统架构方面,可能需要分层设计,比如客户端、网关层和服务端的分片限流。客户端限流可以减少对后端压力,但需要确保流量均衡,否则可能不均匀。网关层可以用像Sentinel这样的中间件集中管理,但百万QPS的话,网关本身会成为瓶颈,可能需要集群部署,同时用一致性哈希来分配请求。 然后,算法选择,令牌桶适合突发流量,漏桶则平滑,高QPS可能更适合令牌桶,但分布式环境下如何同步令牌数?Redis的原子操作可能不够,因为百万QPS下Redis的吞吐量可能不够,可能需要分片存储或者本地缓存结合异步同步。引用里提到的分片式限流,比如每个节点管理一部分限流,这样总分片数相加可以达到更高QPS,但需要协调分片策略,确保流量正确分配到各节点。 数据存储方面,Redis Cluster可以横向扩展,但Lua脚本的执行需要保证原子性,可能需要每个分片处理自己的限流计数,避免跨节点操作。不过这样分片间的数据一致性会有问题,可能需要最终一致性,或者忍一定的误差。比如,每个节点有自己的计数器,定期同步到中心存储,但这样在短时间内可能超过全局限制,需要权衡。 性能优化方面,本地缓存+异步上报可以减轻中心存储的压力,比如在客户端或网关层维护本地计数器,每隔一段时间同步到Redis,这样即使有短暂超出,也能在下一周期调整。但时间窗口的精度会受影响,可能需要滑动窗口算法来更精确控制。 降级也很重要,限流系统本身不能成为单点故障,需要熔断机制,当Redis不可用时,可以降级到本地限流,虽然可能不够准确,但保证系统可用性。另外,动态配置是必须的,能够实时调整限流阈值,应对流量波动。 开源方案中,Sentinel支持集群限流,可能需要结合其Token Server模式,将限流决策集中处理,但扩展性可能受限。而Bucket4j适合客户端集成,结合Redis进行分布式计数,但同样需要考虑Redis的性能瓶颈。 可能的问题包括如何在高并发下保证原子操作,比如使用Redis的INCR和EXPIRE命令,结合Lua脚本确保原子性。另外,分片策略的设计,比如按用户ID哈希分配到不同分片,确保同一用户的请求由同一分片处理,避免跨分片计数。 还要考虑监控和报警,实时跟踪限流状态,及时发现瓶颈。比如Prometheus收集指标,Grafana展示,自动调整限流参数。 总之,设计这样的系统需要结合分层限流、高效算法、分片存储、本地缓存和异步同步,同时保证高可用和能力,可能需要综合多种技术方案,并进行性能测试和调优。</think>### 设计支持百万级 QPS 的分布式限流系统架构及实现方法 #### 一、核心架构设计 1. **分层限流策略** - **客户端限流**:在客户端或 SDK 层实现基础限流逻辑(如计数器或令牌桶算法),过滤无效请求[^2]。 - **网关层限流**:通过 API 网关(如 Nginx + Lua、Envoy、Sentinel)实现全局流量控制,支持滑动窗口、令牌桶等算法[^1]。 - **服务端分片限流**:将限流规则按分片键(如用户 ID、IP 哈希)分布到不同节点,单节点处理局部流量,总分片数满足百万级 QPS[^1][^2]。 2. **算法选择与优化** - **高吞吐场景**:优先选择时间复杂度为 $O(1)$ 的算法(如固定窗口、令牌桶)。 - **精度与平滑性**:采用滑动窗口 + 时间片细分(如 1 秒拆分为 10 个 100ms 子窗口)平衡性能与精度[^1]。 - **分布式一致性**:通过 Redis Cluster 的 `INCR` + `EXPIRE` 实现原子计数,或使用分片限流避免跨节点竞争[^3]。 #### 二、关键技术实现 1. **数据存储与计算分离** - **计数器存储**:Redis Cluster 横向扩展,通过 Pipeline 批量操作减少网络开销,单节点 QPS 可达 10 万级。 - **本地缓存降级**:节点维护本地计数器并异步上报,防止中心存储故障(最终一致性)[^3]。 2. **高性能限流判断逻辑** ```python # 伪代码:基于 Redis 的令牌桶算法实现 def acquire_token(rate, capacity, key): now = time.time() tokens_key = f"{key}:tokens" timestamp_key = f"{key}:timestamp" # 使用 Lua 脚本保证原子性 lua_script = """ local tokens = tonumber(redis.call('get', KEYS[1])) or ARGV[2] local last_refill = tonumber(redis.call('get', KEYS[2])) or 0 local now = tonumber(ARGV[1]) local interval = now - last_refill if interval > 0 then local new_tokens = math.min(ARGV[2], tokens + interval * ARGV[3]) redis.call('set', KEYS[1], new_tokens) redis.call('set', KEYS[2], now) tokens = new_tokens end if tokens >= 1 then redis.call('decr', KEYS[1]) return true end return false """ return redis.eval(lua_script, 2, tokens_key, timestamp_key, now, capacity, rate) ``` #### 三、性能优化手段 1. **批量预取机制**:客户端批量获取令牌(如每次预取 100 个),减少与限流服务器的交互频率。 2. **异步统计上报**:通过内存队列异步聚合统计数据,降低中心存储压力。 3. **分片路由策略**:采用一致性哈希算法分配流量,避免热点分片问题。 #### 四、与监控 1. **熔断降级**:当 Redis 不可用时,自动切换至本地限流模式(牺牲全局一致性)。 2. **动态规则推送**:通过配置中心(如 ZooKeeper、Nacos)实时更新限流阈值。 3. **监控指标**:统计 QPS、限流触发次数、延迟分布(P99/P999),使用 Prometheus + Grafana 可视化。 #### 五、开源方案扩展 - **Sentinel 集群流控**:部署 Token Server 集群,通过 RPC 通信协调全局配额。 - **Bucket4j + Redis**:结合分布式存储实现高精度令牌桶算法(需优化 Lua 脚本性能)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值