高并发系统的三把利器:缓存、限流、降级,利用此3种技术方案即可保系统运行无忧。由于限流是系统的首道关口,所以本文以限流为主题,普及限流算法的基础知识。
限流即限制流量,通过流量控制来保证系统接收到的请求量在正常范围内。由于任何系统的吞吐量都有上限,所以必须设置合理的限定值,以避免流量洪峰将整个系统打垮。
假如一个系统可以承载的网络带宽是1G,如果流量大于1G就会导致带宽打满,影响整个服务。在现实生活中,限流场景也随处可见:例如银行的叫号系统、餐厅的排队系统,如今的疫情,政府也是全力排除隐患,保证医疗系统健康运行。
限流的目的只有一个:保护系统,保证系统在可控的负载下平稳运行。
触发限流的条件:
1. 用户增长过快
2. 热点事件(突发流量)
3. 竞争对象爬虫
4. 恶意的刷单
5. 攻击
常见的限流算法共3种:
1. 计数器算法(固定窗口限流+滑动窗口限流)
2. 漏桶算法
3. 令牌桶算法
每种算法均有其应用场景,下面章节将逐步讲解各个限流算法的原理以及优缺点。
计数器算法是在单位时间内统计用户请求数,一旦请求数量超出设定的阈值,即触发限流策略。计数器算法根据单位时间的计算方式又分为固定窗口算法及滑动窗口算法。
固定窗口算法指每个单位时间相对隔离,一个单位区间的请求量统计跟其他单位区间的请求量统计完全独立。当一个单位时间过期,自动进入下一个时间阶段重新进行计数,固定窗口计数器算法逻辑图如下:
固定窗口计数器算法相对简单,但会存在临界问题(何为临界问题?临界问题即为用户流量并不会像我们所期望的匀速请求,而是可能在某个时间点集中爆发)。如下图所示:在第一个单位时间内的前800ms只有一次请求,后200ms(800ms-1s范围内)内承载999次请求,而第二个单位时间内前200ms承载1001-2000次共1000个请求。这样系统在400ms(800ms到1200ms时间范围内)内共承载了1999次用户请求,此访问量已经远远超出系统所能承载的1000次请求(系统最大QPS),从而给系统带来灾难性后果。
固定窗口计数器算法实现代码(伪代码)如下:
int unitTime = 1s //设置单位时间为1s
int limitCount = 1000 //设置单位时间只能1000次请求
string limitKey = 'limitkey'; //单位时间限流状态key
if(!has(limitKey)){
//初始化单位时间限流状态,并设定期有效期(有效期为一个单位时间)
//参考redis的set命令
set(limitKey,0,unitTime)
}
//原子递增请求量,并返回当前单位时间已有的请求数
int counter = incr(limitKey,1)
if counter>limitCount then
//超出设置的限流规则,直接返回503(也可自定义返回内容)
return 503
else
continue;//继续执行业务逻辑
end
为了解决固定窗口算法的临界问题,我们将其升级为滑动窗口算法。滑动窗口算法实现借鉴滑动窗口协议,将单位时间继续细化为更小粒度的时间网格,每当用户请求,时间网格随之推移,计数器的的统计时间区间也随之变动。滑动窗口算法的单位时间不再是彼此独立,而是步步递进,彼此重叠。这也是滑动窗口算法跟固定窗口算法最大的区别。
(滑动窗口协议主要用于网络数据传输时的流量控制,避免发送网络堵塞 具体可参考https://baike.baidu.com/item/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E5%8D%8F%E8%AE%AE)
如下图所示,滑动窗口算法将计数器算法(固定窗口算法)的单位时间进一步细化,例如将1秒分为10个时间网格,每个网格占用100ms的时间。第一个单位时间为0ms-1000ms(以毫秒为单位)、第二个单位时间为100ms-1100ms、第三个单位时间为200ms-1200ms,以此类推。每个单位时间的请求总量都需小于设定的最大请求量。
由于滑动窗口算法每次都需要统计单位时间的请求量,开销远大于固定窗口算法,所以在真实的业务环境中需要慎重使用滑动窗口算法。
滑动窗口计数器算法实现代码(伪代码)如下:
int unitTime = 1000ms //设置单位时间为1s(1000ms)
int limitCount = 1000 //设置单位时间只能1000次请求
string listKey = 'limitkey'; //单位时间限流状态key
//获取当前毫秒数 demo:1589510627001
int curMilliseconds = nowMilliseconds();
//计算单位时间的起始时间
int startMilliseconds = curMilliseconds-unitTime*1000 //获取单位时间的起始时间
//获取当前往前推1s 之内的所有请求量(这一步及耗性能)
//参考redis的ZCOUNT命令
int counter = ZCOUNT(listKey,startMilliseconds,curMilliseconds)
if counter>limitCount
//超出设置的限流规则,直接返回503(也可自定义返回内容)
return 503
else
ZADD(listKey,curMilliseconds,唯一标识)
continue;//继续执行业务逻辑
end
漏桶算法业务逻辑也相对简单,如下图所示:水滴(用户请求)优先注入到桶中(定长队列、先进先出队列),桶(队列)盛满后自动抛弃(限流)多余的水(请求),另外桶以匀速的方式漏出水滴(处理请求)。
由此可见,漏桶算法以绝对平均的速度处理用户请求,无论用户请求有多大,最终消费用户请求的速率是固定不变的。在真实的业务场景中,漏桶算法可以解决请求毛刺问题(不平均问题),但面对合法的突发流量,漏桶算法就有点捉襟见肘。
漏桶算法的业务逻辑如下:
1、 创建定长队列(demo:长度固定为1000的队列)
2、 用户请求优先入队列(如果队列已满,直接抛弃请求)(入队列速率不限定)
3、 事件调度器以固定速率(demo:1000r/s)消费队列数据,并释放队列资源
漏桶算法实现代码(伪代码)如下:
//代码实现(伪代码)
local rate = 1ms //设置生产速率为1个/ms
local bucketSize = 1000 //漏桶大小 设置可容纳1000个水滴
//初始化漏桶
local bucketQueue = new BucketQueue(bucketSize)
//用户请求
local userRequest = new UserRequest();
local res = bucketQuest.push(userRequest)
if res ==false then
//目前漏桶已满,无法将请求放入漏桶(队列),直接返回503(也可自定义)
return 503
else
//等待事件调度处理
end
//队列消费(每1ms处理一个请求(一滴水))
setTimeout(function () {
local userRequest = bucketQuest.lpop()
//执行业务逻辑
}, rate);
令牌桶算法与漏桶算法有相似之处,都包含2部分业务逻辑。漏桶算法的2部分业务逻辑为:漏桶队列+事件调度器;令牌桶算法的2部分业务逻辑为:令牌生产+令牌消费。漏桶算法是以固定的速率处理用户请求(消费),而令牌桶算法则以固定的速率生产令牌(生产)。
如下图所示:令牌工厂以固定的速率生产令牌并注入到令牌桶中,令牌桶满时会自动抛弃多余的令牌。当有用户请求,优先从令牌桶获取有效令牌(可以理解为打开大门的钥匙),只有获取到令牌的请求才会继续向下执行业务逻辑,否则直接拒绝请求(限流)。其中需要重点关注的是:1、一个令牌不可被2个请求获取(需要加锁,并置为不可用状态);2、在请求处理完毕后需销毁令牌。
令牌桶算法实现代码(伪代码)如下:
//代码实现(伪代码)
local tokenProducerRate = 1ms //设置生产速率为1个/ms
local bucketSize = 1000 //令牌桶大小 设置可容纳1000个令牌
local bucketKey = "bucketKey" //令牌桶名称
bucketKey.setSize(bucketSize) //设置令牌桶大小
local token = bucketKey.getValidToken() //获取有效令牌
if token ~= false then
token.lock() //加锁,需要原子性
else
//超出设置的限流规则,直接返回503(也可自定义)
return 503
end
//继续执行业务逻辑
token.destroy() //令牌销毁
//令牌工厂代码(每1ms向令牌桶推送一个令牌)
setTimeout(function () {
local token = new Token();
local result = bucketKey.push(token)
if result == false then
//向令牌桶已满,push命令失败
else
//向令牌桶未满,push成功
end
}, tokenProducerRate);
每种算法都有其可取的优点,在真实的业务环境需要结合实际情况确定使用哪种限流算法,也可对各类算法聚合使用,满足更多的业务场景。
注:本文以基础理论为主讲解各类算法的原理以及实现方案,后期将结合业务场景分别讲解nginx的限流策略以及google的guava限流策略。
本文由哈喽比特于4年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/MA1CLXdknLvnPV_s3ZQUSg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。