前言
当资源成为瓶颈时,服务框架需要对消费者做限流,启动流控保护机制。流量控制有多种策略,比较常用的有:针对访问速率的静态流控、针对资源占用的动态流控、针对消费者并发连接数的连接控制和针对并行访问数的并发控制。
常见限流方式
静态流控
主要针对客户端访问速率进行控制,它通常根据服务质量等级协定(SLA)中约定的QPS做全局流量控制,例如订单服务的静态流控阈值为100 QPS,则无论集群有多少个订单服务实例,它们总的处理速率之和不能超过100 QPS。
动态流控
它的最终目标是为了保命,并不是对流量或者访问速度做精确控制。当系统负载压力非常大时,系统进入过负载状态,可能是CPU、内存资源已经过载,也可能是应用进程内部的资源几乎耗尽,如果继续全量处理业务,可能会导致长时间的Full GC、消息严重积压或者应用进程宕机,最终将压力转移到集群其它节点,引起级联故障。触发动态流控的因子是资源,资源又分为系统资源和应用资源两大类,根据不同的资源负载情况,动态流控又分为多个级别,每个级别流控系数都不同,也就是被拒绝掉的消息比例不同。每个级别都有相应的流控阈值,这个阈值通常支持在线动态调整。
并发控制
针对线程的并发执行数进行控制,它的本质是限制对某个服务或者服务的方法过度消费,耗用过多的资源而影响其它服务的正常运行。并发控制有两种形式:针对服务提供者的全局控制和针对服务消费者的局部控制。
连接控制
通常分布式服务框架服务提供者和消费者之间采用长连接私有协议,为了防止因为消费者连接数过多导致服务端负载压力过大,系统需要支持针对连接数进行流控。
分布式限流
以上限流方式只能针对单机进行限流,无法根据实际需要进行流量限制,接下来介绍一下分布式限流方案。
计数服务
- 开发一个技术服务模块
- 配置模块进行限流额度配置
- 每次请求上报到 redis
- 进行请求计数统计
- 限流模块进行超额判断
问题:
- Redis 单点问题
- 每次请求都要上报,多一次缓存的网络请求,对性能有影响
- 固定配额,不够灵活
计数服务2.0
图源:极客时间- go 编程训练营
- 管理后台配置额度
- 服务端分配额度
- API网关请求额度
- API 网关基于令牌桶算法本地进行限流
- 本地进程异步上报令牌,服务端根据管理后台配置和算法重新分配限额
分布式限流
最大最小分配算法
- 初始分配默认配额
- 一旦有历史窗口的值可以基于历史窗口的值统计进行配额请求
- 如上图所示
- 假设起初总配额是10
- A请求2、B请求2.6、C请求4、D请求5配额
- 先进行平均分配,10/4 每人分配 2.5 个配额
- A只需要2个,所以分配2,多出0.5进一步分配
- 0.5/3 + 2.5, B、C、D 各分配2.666
- B 只申请了 2.6 个配额,所以多出 0.06 个配额
- 0.06/2 + 2.666 , C、D 分配 2.7 个配额
- 分配结束,每个服务都尽量做到了公平的资源最大分配
加权最大最小分配算法
- 有四个用户,A、B、C、D 分别申请资源 2、4、4、10
- 权重分别为 4、2.5、1、0.5
- 资源总量16
- 对权重进行标准化分别乘10,再化简,得到:8、5、2、1
- A 分得配额8、B 5、C 2、D 1
- 由于A 只需要资源2、B只需要资源4,因此A+B多出资源 7
- 多出的资源再分配给C、D, C=2+7*(2/3)、D=1+7*(1/3)
- 一次C获得的资源为 6.666, D获得的资源为3.333
- 将C多出的 6.666-4=2.222 再次分配给D
- 所以D获得的资源为 2.222+3.333=5.555
Reference
- 微服务治理的技术演进和架构实践
- max-min fairness 最大最小公平算法