SaaS 电商设计 (十一) 那些高并发电商系统的限流方案设计

目录

  • 一.什么是限流
  • 二.怎么做限流呢
    • 2.1 有哪些常见的系统限流算法
      • 2.1.1 固定窗口
      • 2.1.1 滑动窗口
      • 2.1.2 令牌桶
      • 2.1.3 漏桶算法
    • 2.2 常见的限流方式
      • 2.2.1 单机限流&集群限流
      • 2.2.2 前置限流&后置限流
    • 2.3 实际落地是怎么做的
      • 2.3.1 流量链路
      • 2.3.2 各链路限流
        • 2.3.2.1 网关层
        • 2.3.2.2 nginx 层
        • 2.3.3.3 服务本身的限流

一.什么是限流

应用系统在自身有限资源下,在面对流量突发场景下的自我保护.这可能是最初的一个目标.在此之上,怎么在有限的资源下去完成系统的最好的性能表现.
那么总结下其实有两个原则.

- 系统保护(主要目标)
- 有限资源下的系统最佳吞吐(理想目标)

有了这两个原则,那么其实我们后续关注的就是如何友好的落地这些点.

二.怎么做限流呢

2.1 有哪些常见的系统限流算法

2.1.1 固定窗口

描述:这是一种简单的限流算法,它根据一个固定的时间窗口来控制流量.在给定时间窗口内,它允许固定数量的请求通过,超出的请求将被丢弃.

实现步骤:

  • 初始化一个计数器(counter)为0,并设置一个固定的时间窗口(window).
  • 当收到一个请求时,检查计数器的值.
  • 如果计数器小于限制的请求数量(limit),则允许请求通过并增加计数器的值.
  • 如果计数器已经达到限制的请求数量,丢弃请求或者返回一个错误.
  • 如果请求的时间超过了时间窗口的结束时间,重置计数器为1,并更新时间窗口为当前时间窗口.
limit = 100  # 限制的请求数量
window = 60  # 时间窗口为60秒def handle_request():global counter, window_start_time//开始时间current_time = time.time()//60 秒的窗口if current_time > window_start_time + window:counter = 0//重置开始时间window_start_time = current_timeif counter < limit:counter += 1# 处理请求else:# 丢弃请求或返回错误

相比之下 固定窗口算法的问题在于,无法应对在流量在突发场景下的冲击.在固定窗口下.el:10:00:00 - 10:00:01 时间窗口 60s. 限流值为 1w.
现实过程可能是如下这种场景.
在这里插入图片描述

这种场景对于固定窗口算法来说,造成的问题就是大量的请求都集中在固定窗口的某一段小窗口时间,导致窗口的 pass count 快速被消耗.这样即使后续的平滑流量甚至小
流量都难以继续通过窗口.

2.1.1 滑动窗口

描述:这是一种基于固定窗口算法优化之后的限流算法,在固定窗口算法的基础上引入了滑动窗口的概念.它允许窗口内的请求速率平滑地变化,而不仅仅是在固定窗口内进行限制.

实现步骤:

  • 初始化一个计数器(counter)为0,并设置一个滑动窗口的时间长度(window)和一个固定时间间隔(interval).
  • 当收到一个请求时,检查计数器的值.
  • 如果计数器小于限制的请求数量(limit),则允许请求通过并增加计数器的值.
  • 如果计数器已经达到限制的请求数量,丢弃请求或者返回一个错误.
  • 每隔固定时间间隔,将计数器的值减去窗口内的请求数量,并更新窗口内的请求数量.
limit = 100  # 限制的请求数量
window = 60  # 滑动窗口的时间长度为60秒
interval = 10  # 固定时间间隔为10秒def handle_request():global counter, window_start_time, window_requestscurrent_time = time.time()if current_time > window_start_time + window:counter -= window_requests.pop(0)window_requests.append(0)window_start_time = current_timeif counter < limit:counter += 1window_requests[-1] += 1# 处理请求else:# 丢弃请求或返回错误

2.1.2 令牌桶

描述:原理是系统以恒定的速率产生令牌,并将这些令牌放入一个桶中.当有请求到达时,需要从桶中获取一个令牌才能继续处理该请求.如果桶中没有足够的令牌,则请求被拒绝.

特点:既能够面对突发的流量峰值,也能处理平滑的系统请求.

具体来说,令牌桶算法的实现包括两个关键参数:令牌生成速率(rate)和桶的容量(capacity).令牌生成速率确定了每秒产生的令牌数量,桶的容量确定了桶中最多可以存放多少个令牌.

import timeclass TokenBucket:def __init__(self, rate, capacity):self.rate = rate  # 令牌生成速率(令牌/秒)self.capacity = capacity  # 桶的容量(令牌数量)self.tokens = 0  # 当前桶中的令牌数量self.last_time = time.time()  # 上次令牌生成时间def get_token(self):now = time.time()elapsed = now - self.last_time  # 计算时间间隔self.last_time = nowself.tokens = min(self.tokens + elapsed * self.rate, self.capacity)  # 生成令牌if self.tokens >= 1:self.tokens -= 1return Trueelse:return False# 示例用法
bucket = TokenBucket(rate=1, capacity=5)  # 令牌生成速率为1个/秒,桶的容量为5个令牌
for i in range(10):if bucket.get_token():print(f"请求 {i+1} 被处理")else:print(f"请求 {i+1} 被限流")time.sleep(1)  # 等待1秒再重新尝试获取令牌

2.1.3 漏桶算法

漏桶算法(Leaky Bucket Algorithm)是一种流量控制算法,用于平滑网络流量.它模拟了一个漏桶,当网络流量超过桶的容量时,多余的流量会被丢弃或者缓存,以保持流量的稳定性.

漏桶算法的原理是,维护一个固定容量的漏桶,每次流量到达时,先将流量放入漏桶中.如果漏桶已满,则丢弃超出容量的流量;如果漏桶未满,则将流量处理完成.然后,漏桶以固定速率漏水,即以固定速度释放流量.这样,即使流量突然增大,漏桶也可以平滑地处理流量.

class LeakyBucket:def __init__(self, capacity, rate):self.capacity = capacity  # 漏桶容量self.rate = rate  # 漏水速率self.water = 0  # 当前水量self.last_leak_time = time.time()  # 上次漏水时间def process(self, amount):current_time = time.time()time_passed = current_time - self.last_leak_timeself.water = max(0, self.water - self.rate * time_passed)  # 漏水if self.water + amount <= self.capacity:self.water += amountself.last_leak_time = current_timereturn True  # 流量处理成功else:return False  # 漏桶已满,丢弃流量

令牌桶算法更适合平滑限制流量,可以灵活地处理不同请求的流量需求;而漏桶算法更适合于固定速率的流量处理,可以有效地限制请求的处理速度 .对应到实际项目过程中,
漏桶的特点的就是恒定流速,令牌的特点就是可以应对突击的流量,应对秒杀大促等的场景.

2.2 常见的限流方式

2.2.1 单机限流&集群限流

单机限流和集群限流是常见的流量控制方式,它们在实现和应用上有一些区别,并且各自具有不同的优缺点.下面是单机限流和集群限流的区别以及它们的优缺点:

  • 区别:

单机限流:在单个服务器或节点上进行限流,仅对该节点的流量进行控制.
集群限流:在整个集群或多个服务器上进行限流,可以对整个系统的流量进行控制.

  • 单机限流的优缺点:

    • 优点:

实现简单:单机限流通常只需要在单个节点上实现限流逻辑,不需要进行节点间的数据共享和协调.
高效低延迟:限流操作只涉及当前节点,因此具有较高的效率和低延迟.

  • 缺点:

有限的扩展性:单机限流的处理能力受限于单个节点的性能,当流量过大时,单个节点可能无法承受.

  • 集群限流的优缺点:

    • 优点:

全局控制能力:集群限流可以在整个集群或多个节点上进行流量控制,实现全局的流量控制策略.
高可伸缩性:通过增加节点来扩展处理能力,可以更好地应对高流量的情况.

  • 缺点:

复杂性增加:集群限流需要在多个节点之间进行数据共享和协调,增加了系统的复杂性.
网络开销:集群限流需要节点间的通信和协调,可能会带来一定的网络延迟和额外的开销.

总的来说单机限流适用于对单个节点或服务器进行流量控制的场景,简单、高效,但扩展性有限;
集群限流适用于对整个系统或多个节点进行流量控制的场景,具有全局控制能力和高可伸缩性,但复杂度和网络开销较高.
选择合适的限流方式应根据具体的应用需求和系统架构来决定.

2.2.2 前置限流&后置限流

前置限流和后置限流是两种常见的流量控制方式,它们在应用时机和实现方式上有一些区别,并且各自具有不同的优缺点.

区别:
前置限流:在请求到达服务之前进行流量控制,即在请求进入系统之前进行限流处理.
后置限流:在请求离开服务之后进行流量控制,即在请求处理完成后进行限流处理.

前置限流的优缺点:

  • 优点:

提前过滤请求:前置限流可以在请求进入系统之前进行过滤和拒绝,减轻服务的负担.
快速响应:由于限流操作在请求进入系统之前进行,可以快速地进行限流判断,避免不必要的请求处理.

  • 缺点:

精确度有限:前置限流通常只能基于部分请求信息进行限流判断,无法对具体请求的处理结果进行评估.
需要额外资源:前置限流需要在请求进入系统之前进行处理,可能需要额外的流量控制组件或网络设备.

后置限流的优缺点:

  • 优点:

基于实际处理结果:后置限流可以根据请求的实际处理结果进行限流判断,更加准确地控制流量.
动态调整:后置限流可以根据实时的系统负载和性能情况,动态调整限流策略.

  • 缺点:

延迟较高:后置限流需要等待请求处理完成后才能进行判断和限流操作,可能会引入较高的延迟.
需要额外资源:后置限流需要在请求处理完成后进行处理,可能需要额外的流量控制组件或网络设备.

从上面的描述知道前置限流适用于在请求进入系统之前进行快速的流量控制,能够提前过滤请求和快速响应,但精确度有限;
后置限流适用于根据请求的实际处理结果进行流量控制,能够基于实际情况进行动态调整,但可能引入较高的延迟.

2.3 实际落地是怎么做的

2.3.1 流量链路

在介绍实际的限流方案需要简单介绍下整体流量链路.

在这里插入图片描述

以上是大致的一个流量链路.

step1:从整个入口到网关是整体流量的入口.
step2:从入口到各个 servicenginx
step3:从 nginx 再到本地的 service 接口

目前系统提供的服务分为两个部分.http 接口以及 rpc 接口.先说 http 接口这一块.这块主要支撑的是整个
主站的服务.具体的协议也是 http 的形式.通过反向代理挂载每一个逻辑分组(一个分组多个机器).

2.3.2 各链路限流

2.3.2.1 网关层

首先是整个网关这一层
这一层主要是用来代理整个公司级别的服务.这一层目前是用后置限流来处理进行处理的.

后置限流:后置限流是指在请求处理完之后,再根据请求的处理结果来判断是否需要限制请求的处理速率.即先处理请求,然后根据处理结果来决定是否限流.
这种方式可以确保请求的处理逻辑不受限流策略的影响,但可能会导致一些请求在处理完后才被限制,从而可能导致系统的负载过大.

前置限流:前置限流是指在请求处理之前,根据系统的负载情况或者其他指标来判断是否需要限制请求的处理速率.即在处理请求之前就根据限流策略来决定是否拒绝该请求.
这种方式可以避免系统负载过大,但可能会导致一些请求被提前拒绝,从而可能影响系统的正常运行.

通过上面的描述能知道后置限流的逻辑是其实目的是不干扰整个业务逻辑的执行,但是在整体流量的防控上做一次兜底.其实本质是没有做到下游的系统保护,这种方式的限流对象
仅仅是流量,就没有系统保护这一层的能力.

2.3.2.2 nginx 层

第二层是nginx 到服务这一层.

这一层目前是有两个维度.
根据 request 请求的方法指定方法请求级别的限流.
这个基本是对于单机限流的一个兜底,在服务的集群限流可能出现的一些漂移现象后的系统保护.
还有一个维度是每次请求都通过 lua 获取本地的物理机 cpu 指标. 具体的限制指标度量,可以依照压测对于 cpu 的观测.

-- 执行系统命令并获取输出结果
function execute_command(command)local handle = io.popen(command)local result = handle:read("*a")handle:close()return result
end-- 获取系统的CPU信息
function get_cpu_info()local cpu_info = execute_command("cat /proc/cpuinfo")return cpu_info
end-- 示例代码
local cpu_info = get_cpu_info()
print(cpu_info)
2.3.3.3 服务本身的限流

上面的限流基本是在服务本身之外的.或网关或流量代理实现的.
对于大型的分布式系统一般是选用集群限流.能很好的因为一些节点的故障带来的整体流量的承载能力的下载如果是单机限流,在节点故障后相当于整体的系统负载能力就削弱了.

具体的限流阈值有两个点.

  • 或者根据相应的往年的监控流量值来进行推算.
  • 具体根据压测时的阈值来进行调整.

第一点需要系统具备完整的监控体系.以获取足够的历史数据来进行推演.有的同学说了,我的系统或者我的接口是第一次上线无法获取足够的历史监控来推测.
系统第一次上线的场景:只能与 qa 测试这边打一个配合,完整的链路压测来彻底了解你的系统.获取到系统在可接受的接口性能下如:200ms.下的最大 单机qps或整体的集群最大 qps.
如果是单机压测,那么集群的限流就是线上机器数*单机承压的 qps 了.

还有一个点对于集群限流的场景,还是觉得有必要 mark 下.对于 rpc 服务来说,特别是上下游关系复杂调用方较多.这时候有单纯的一个集群限流
往往解决不了问题.很可能需要根据不同的调用方来实现不同的集群限流.例如:对于核心的,量大的 调用方单独的集群限流.如 2 个这种调用方就需要两个
集群限流 key.其他的一些小流量这样可以归并到 other 这样的集群限流渠道就好了.

集群限流的实现方式 sentinel

预取率:指的是在集群限流场景下.本地的调用端对于集群 token 的预取占比.举个例子:集群限流是 10000.预取:7000.那么预取率则是 70%.
假如有 70 台机器.则每个机器的 token 预取个数则是 10.

目前集群限流的过程

在这里插入图片描述

通过图示可以看出图上为令牌桶限流算法.
通过平台设置指定 key 的限流阈值.
consumer 启动完成 token 预取.预取的逻辑很常见.(如:美团的 leaf 的框架也有设计.好的框架总是借鉴来借钱去,这里也是套用电影里的说法
致敬)
实际请求过程中通过token 验证通过,完成对 provider 的调用.验证不通过则触发限流.

赠人玫瑰 手有余香 我是柏修 一名持续更新的晚熟程序员
期待您的点赞,关注加收藏,加个关注不迷路,感谢
您的鼓励是我更新的最大动力
↓↓↓↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/339886.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Maven 中的 classifier 属性用过没?

最近训练营有小伙伴问到松哥一个关于 Maven 依赖的问题&#xff0c;涉及到 classifier 属性&#xff0c;随机问了几个小伙伴&#xff0c;都说工作中没用到过&#xff0c;因此简单整篇文章和小伙伴们分享下。 Maven 大家日常开发应该都有使用&#xff0c;Maven 中有一个比较好玩…

图论(四)—最短路问题(Dijkstra)

一、最短路 概念&#xff1a;从某个点 A 到另一个点B的最短距离&#xff08;或路径&#xff09;。从点 A 到 B 可能有多条路线&#xff0c;多种距离&#xff0c;求其中最短的距离和相应路径。 最短路径分类&#xff1a; 单源最短路&#xff1a;图中的一个点到其余各点的最短路径…

【图像处理与机器视觉】频率域滤波

知识铺垫 复数 CRjI 可以看作复平面上的点&#xff0c;则该复数的坐标为&#xff08;R&#xff0c;I&#xff09; 欧拉公式 e j θ c o s θ j s i n θ e^{j\theta} cos \theta j sin \theta ejθcosθjsinθ 极坐标系中复数可以表示为&#xff1a; C ∣ C ∣ ( c o s…

15、matlab绘图汇总(图例、标题、坐标轴、线条格式、颜色和散点格式设置)

1、plot()函数默认格式画图 代码: x=0:0.1:20;%绘图默认格式 y=sin(x); plot(x,y) 2、X轴和Y轴显示范围/axis()函数 代码: x=0:0.1:20;%绘图默认格式 y=sin(x); plot(x,y) axis([0 21 -1.1 1.1])%设置范围 3、网格显示/grid on函数 代码: x=0:0.1:20;%绘图默认格式 …

拓展虚拟世界边界,云手机可以做到吗

虚拟世界&#xff0c;AI&#xff0c;VR等词汇是21世纪最为流行的词汇&#xff0c;在科技背后&#xff0c;这些词汇的影响变得越来越大&#xff0c;已经走进了人们的世界&#xff0c;比如之前APPLE发布的vision pro&#xff0c;使人们能够更加身临其境的体验到原生os系统&#x…

从MLP到卷积

1.从MLP到卷积层 最近要做多通道的实验&#xff0c;所以重新将处理图像的基础模型回顾一下&#xff0c;什么是卷积&#xff1f;卷积本质是是一种特殊的全连接层。 1.1怎么w的权重从一个值变成了4维呢?可以这样理解&#xff0c;在此举一个例子&#xff1a; 其实本质可以看成&…

安防综合管理系统EasyCVR平台GA/T1400视图库:基于XML的消息体格式

GA/T 1400标准的应用范围广泛&#xff0c;涵盖了公安系统的视频图像信息应用系统&#xff0c;如警务综合平台、治安防控系统、交通管理系统等。在视频监控系统中&#xff0c;GA/T 1400公安视图库的对接是实现视频图像信息传输、处理和管理的重要环节。 以视频汇聚EasyCVR视频监…

Flink中因java的泛型擦除导致的报错及解决

【报错】 Exception in thread "main" org.apache.flink.api.common.functions.InvalidTypesException: The return type of function Custom Source could not be determined automatically, due to type erasure. You can give type information hints by using th…

使用Xshell一键在多个会话中执行多个命令

背景 平时在工作中经常通过ssh远程操作Linux&#xff0c;由于我们负责的服务部署在超过5台服务器&#xff08;相同的代码及路径&#xff09;&#xff0c;每次发布后执行重启都得重复操作5次关闭、检查、启动、查看日志&#xff0c;特别繁琐。 后来发现Xshell 7可以录制脚本&am…

【MyBatis】MyBatis操作数据库(二):动态SQL、#{}与${}的区别

目录 一、 动态SQL1.1 \<if>标签1.2 \<trim>标签1.3 \<where>标签1.4 \<set>标签1.5 \<foreach>标签1.6 \<include>标签 二、 #{}与${}的区别2.1 #{}是预编译sql&#xff0c;${}是即时sql2.2 SQL注入2.3 #{}性能高于${}2.4 ${}用于排序功能…

洛谷 P10566 「Daily OI Round 4」Analysis 题解

先弄个 ASCII 码表&#xff1a; 分析 很明显&#xff0c;想要节省时间&#xff0c;就要把这些字符转换成和它们的 ASCII 值最接近的大写字母。 通过 ASCII 码表&#xff0c;很容易就可以发现&#xff1a; ASCII 值与数字最接近的大写字母是 A \texttt A A。ASCII 值与小写…

JVM学习-监控工具(二)

jmap&#xff1a;导出内存映像文件&内存使用情况 基本情况 jmap(JVM Memory Map)&#xff1a;一方法获取dump文件(堆转储快照文件&#xff0c;二进制文件)&#xff0c;还可以获取目标Java进程的内存相关信息&#xff0c;包括Java堆各区域的使用情况、堆中对象的统计信息、…

多线程新手村5--线程池

1.1 线程池是什么 线程诞生的意义是因为进程的创建/销毁开销太大&#xff0c;所以使用线程提高代码的执行效率&#xff1b;那如果想要进一步提升执行效率&#xff0c;该怎么办呢&#xff1f;有一个方法是使用线程池。 首先&#xff0c;什么是线程池&#xff1a;池就是池子&am…

奶茶店、女装店、餐饮店是高危创业方向,原因如下:

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 现在的俊男靓女们&#xff0c;心中都有一个执念&#xff1a; (1)想证明自己了&#xff0c;开个奶茶去…… (2)想多赚点钱了&#xff0c;加盟餐饮店去…… (3)工作不顺心了&#xff0c;搞个女装店去…… 但凡抱着…

102.网络游戏逆向分析与漏洞攻防-ui界面的设计-反隐身功能的界面设计与实现(有不使用MFC生成,自己手写代码创建复选框与事件的例子)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

独立游戏开发的 6 个步骤

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

discuz论坛怎么修改备案信息

大家好&#xff0c;今天给大家分享下discuz如何填写备案信息并且展示在网站首页。大家都知道国内网站都需要备案&#xff0c;不通过备案的网站上是没办法通过域名打开的。大家也可以通过搜索网创有方&#xff0c;或者直接点击网创有方 查看悬挂备案号后的效果。 首先大家可以看…

MySQL表的增删改查初阶(上篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

成人本科毕业论文怎么写?分享自己的经验

撰写成人本科毕业论文是一个系统而深入的过程&#xff0c;以下是我个人的经验分享&#xff0c;希望能帮助你更好地完成这一任务&#xff1a; 1. 明确论文选题 兴趣与专长&#xff1a;选择自己感兴趣且有一定专长的领域&#xff0c;这样更容易深入研究。可行性&#xff1a;确保…

【全开源】种草分享|动态朋友圈|瀑布流|uniapp

一款基于FastadminThinkPHP和Uniapp开发的种草分享评论点赞消息提醒系统&#xff0c;发布动态&#xff0c;分享种草生活&#xff0c;可以收藏关注点赞&#xff0c;消息提醒&#xff0c;同时支持H5/小程序/app多端。 ​让每一次互动都不再错过&#x1f514; &#x1f331; 种草…