分布式限流

146 阅读3分钟

在现实情况下,当面对极端的业务场景时,瞬时的业务流量会带来大大超出系统真实容量的压力,就会给服务器造成很大压力,可能导致服务器无法提供其它服务。保证系统的稳定性,通常采取的策略就是限流降级,以保障承诺容量下的系统稳定;同时还有业务层面的开关预案执行,峰值时刻只保障核心业务功能,非核心业务功能就关闭。

限流:根据某个应用或基础部件的某些核心指标,如QPS或并发线程数,来决定是否将后续的请求进行拦截。比如我们设定1秒QPS阈值为200,如果某一秒 的QPS为210,那超出的10个请求就会被拦截掉,直接返回约定的错误码或提示页面。

降级:它的作用是通过判断某个应用或组件的服务状态是否正常,来决定是否继续提供服务。以RT(响应时间)举例,我们根据经验,一个应用的RT在50ms以内,可以正常提供服务,一旦超 过50ms,可能就会导致周边依赖的报错或超时。所以,这时我们就要设定一个策略,如果应用的RT在某段时间内超过50ms的调用次数多于N次,那该应用或该应用的某个实例 就必须降级,不再对外提供服务,可以在静默一定时间后(比如5s或10s)重新开启服务。

熔断: 1696953018502.png 限流算法: [【限流算法】大白话阐述四种限流算法_原子类限流只是单体_ADAMs.的博客-CSDN博客] (blog.csdn.net/qq_43097201…)

限流和熔断的区别:

  1. 目标不同: 限流是限制系统的请求和并发数确保不会因为过多请求导致服务过载。
    熔断是系统某个服务or组件发生故障时,熔断暂时中断请求防止故障传播

分布式令牌桶限流算法的实现

学习:日拱一卒的Alex
1701238589414.png

1701238603831.png

定时任务实现

桶满溢出、桶空堵塞or丢弃

数据结构: 令牌桶:List;令牌:唯一标识uuid;

生产者: 定时任务:以interval秒的间隔往令牌桶中放入r个令牌 1701239916170.png

消费者: 1701240022178.png

定时任务调度具体实现:

  1. Redis方法解决 expire设置key过期时间 ——> 发布订阅接收到key的过期提醒 ——> 接收过期提醒操作

  2. 额外运行一个定时服务 例如工具:golang里面的Timer和Ticker。

    Timer和Ticker用法:Golang 定时器(Timer 和 Ticker ),这篇文章就够了 - 掘金 (juejin.cn)

    存在问题:单台机器部署可靠性无法保证、多台机器部署会重复触发。
    解决:多台机器部署只执行一次,通过redis分布式锁(key(唯一就行)和过期时间(业务是保证每interval秒只有一台触发,设置为小于但接近interval的时间)的设置)。

分布式限流的实现

把全局计数或令牌放在一个所有实例都能快速、一致访问的外部存储 基于Redis的令牌桶:

  • 存储:桶状态(剩余令牌数、上次填充时间、桶容量、填充速率)存在Redis 存储在Redis的一个Hash结构里面,key设置为:rate_limit:业务标识

  • Lua原子性:每次请求先用Lua脚本原子地“检查令牌+消费”,避免并发竞争

  • Why Redis:

    • 高并发读写
    • Lua脚本原子性
    • 过期
    • 所有服务节点共享同一份限流状态,保证全局一致性 Redis高可用部署