当前位置: 首页 > news >正文

如何设计一个限流器?

一、限流器理解

限流可以在客户端、服务端,也可以在API网关

不同的限流算法有不同的优点和缺点,要结合场景使用,常见的有桶代币算法、漏桶算法、固定窗口技术器算法、滑动窗口日志算法。

二、限流算法

2.1.1 桶代币算法

桶代币是一个有预定义容量的容器,代币按照预定的速度被放如桶中,一旦桶满则不再放入。然后每个请求将会消耗一个代币,如果请求到达时没有足够的代币请求就会被丢弃。

桶代币算法的两个关键参数:桶大小、重新注入代币的速度

2.1.2 漏桶算法

与桶代币算法类似,但它对请求是按照固定速率处理的。漏桶算法通常采用先进先出的队列来实现。

漏桶算法两个关键参数:桶大小、出栈速度

2.1.3 固定窗口计数器算法

工作原理:将时间轴分成固定大小的窗口,并给每个窗口分一个计数器。每到达一个请求,计数器就会值+1,一旦计数器达到预设的阈值,新请求就会被丢弃,直到开始一个新的时间窗口。

比如时间窗口1s,阈值3,则如图:

滑动窗口计数器算法缺点是:在时间窗口的边界上流量激增,会导致通过的请求数超过设定的阈值。

2.1.4 滑动窗口日志算法

工作原理:记录每个请求的时间戳(通常保存在缓存中,如Redisd的有序集合);当新请求到达时,移除所有过期时间戳(指早于当前时间窗口开始时间的时间戳),将新请求的时间戳添加到日志中,如果日志的条数小于等于允许的条数则允许请求通过:

优点是实现的流量控制非常准确,在任何滑动的时间窗口请求的数量都不睡超过阈值,内存使用很高效。但缺点是:算法是根据真实流量速率。

三、拒绝策略和设计

3.1 单体限流器设计

如果一个请求被限流,API会给客户端返回HTTP响应码429(请求过多)。根据应用场景,也有可能会把超过阈值的请求放如队列,之后再处理。下图是一个单体应用的设计示例:

上图流程简要说明:

  • 流量限制规则存储在硬盘上,工作进程经常从硬盘中获取规则并将其存储到缓存中;
  • 当客户端向服务端发送请求时,请求会首先被发给限流中间件;
  • 限流中间件从缓存中加载规则,它从Redis缓存中获取计数器和上一次请求的时间戳;
  • 基于响应,限流中间件做出不同的决策:
    • 如果没有被限流则转发请求给API
    • 如果请求被限流,限流器会向客户端返回429响应码报错,同时要么丢弃请求要么被转发到队列中去。

3.2 分布式系统限流

单体服务环境中创建限流器并不难,但是要将限流器系统扩展,以支持多个服务器和并发线程,就是另一回事了,其中存在两个挑战:竞争条件 和 同步问题。

3.2.1 竞争条件

可能会发生在一个高并发的环境中,锁的竞争是最直观的解决方案,但是它会显著拖慢系统速度。

解决这个问题可以使用的方案:Lua脚本 + Redis有序集合数据结构

local tokens_key = KEYS[1]   -- 令牌数Key
local timestamp_key = KEYS[2] -- 时间戳Key
local rate = tonumber(ARGV[1]) -- 速率
local capacity = tonumber(ARGV[2]) -- 容量
local now = tonumber(ARGV[3])     -- 当前时间
local requested = 1               -- 请求令牌数local last_tokens = tonumber(redis.call("GET", tokens_key)) or capacity
local last_time = tonumber(redis.call("GET", timestamp_key)) or nowlocal elapsed = now - last_time
local new_tokens = math.min(capacity, last_tokens + elapsed * rate)if new_tokens < requested thenreturn 0
elseredis.call("SET", tokens_key, new_tokens - requested, "EX", window_sec*2)redis.call("SET", timestamp_key, now, "EX", window_sec*2)return 1
end

3.2.2 同步问题

在百万级量级的系统中,一个限流器不足以处理所有流量,这时使用多个限流器的话,就要解决限流器之前的同步问题。一个可行的解决方案就是使用粘性会话,允许客户端将请求总是发往同一个限流器,但是这个方案不好扩展也不灵活。更好的方法是使用中心化的数据存储,比如Redis:

四、性能优化

上面的内容其实已经把关键点说了七七八八了,但是初步的设想肯定是存在优化空间的,而且优化问题也是面试中最为常见的主题,因此再针对两个方面进行优化:

(1)对限流器而言,设置多数据中心是至关重要的,因为离数据中心越远,响应耗时越大;

(2)通过最终一致性模型来同步数据

(3)做好流量限制算法的监控,及时发现限流算法对于使用场景的匹配性,如果发现不适合则需要切换另一种算法。

最终一致性模型扩展:

    一致性模型是设计键值存储系统是需要考虑的一个重要因素,一致性模型分几种不同类型,可以根据系统需求和应用场景来选择合适的一致性模型。

  • 强一致性模型:任何读操作返回的值都是最新写入的数据,客户端永远不会看见过时的数据。通常是通过强制一个副本在当前写入操作成功前不再接收新的读/写操作来实现的,所以很明显性能不会太好,不适合。
  • 弱一致性模型:随后的读操作返回的可能不是最新的值;
  • 最终一致性模型:这是弱一致性的一种特殊形态。经过足够长的时间后,所有的数据都会更新传播开来,并且所有副本会变得一致。可以了解一下MYSQL的隔离级别那块,通过版本控制思路来实现,或者MQ订阅方式实现。

http://www.dtcms.com/a/306760.html

相关文章:

  • 【35】C# WinForm入门到精通 —— label 控件 不能 鼠标调整大小,解决办法
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——8. AI赋能(下):在Qt中部署YOLOv8模型
  • WD8001 专为小功率风扇供电及控制而生,低成本,为小风扇生产厂商寻求创新解决方案
  • day065-ALB负载均衡与云盘扩容
  • conda 安装prokka教程
  • PyCharm功能及安装使用教程
  • JavaWeb 核心:AJAX 深入详解与实战(Java 开发者视角)
  • 8. 网络层
  • 2025最新版Node.js下载安装及环境配置教程【超详图文】
  • 项目里程碑如何设定?策略分析
  • 【vLLM学习笔记】:vLLM 参数说明
  • 三十四、【Linux常用工具】rsync+inotify实时同步演示
  • rsync+sersync实现文件实时同步
  • 防火墙与入侵检测
  • 聊聊如何判断发现的缺陷属于前后端
  • 一洽客服系统:Web路由策略
  • LSM树Python实现深度解析:从理论到实战的全方位探索
  • Chroma安装教程
  • PCB学习笔记(一)
  • Python 程序设计讲义(42):组合数据类型——元组类型:创建元组
  • AI Agent管理后台原型设计全拆解(附3套可复用素材)
  • 达梦(DM8)常用管理SQL命令(1)
  • python可视化:按序号展示社团星级分布 - 热力图样式
  • Vulnhub靶场:ica1
  • VUE -- 基础知识讲解(二)
  • 《Java 程序设计》第 11 章 - 泛型与集合
  • 迪丽热巴写真壁纸
  • 【38】WinForm入门到精通 ——WinForm平台为AnyCPU 无法切换为x64,也无法添加 x64及其他平台
  • Apache Ignite 中如何配置和启用各类监控指标
  • 还在使用Milvus向量库?2025-AI智能体选型架构防坑指南