96SEO 2026-02-19 22:30 16
尤其是C端系统#xff0c;经常面对海量用户请求#xff0c;如不做限流#xff0c;遇到瞬间高并发的场景#xff0c;则可能压垮系统…作者京东科技

尤其是C端系统经常面对海量用户请求如不做限流遇到瞬间高并发的场景则可能压垮系统。
如某类交易任务处理时有速率要求再如上下游调用时下游对上游有速率要求。
•无论哪种场景都需要对请求处理的速率进行限制或者单个请求处理的速率相对固定或者批量请求的处理速率相对固定见下图
维护最大的并发请求数如连接数当并发请求数达到阈值时报错或等待如线程池。
以固定速率向桶内发放令牌。
请求处理时,先从桶里获取令牌只服务有令牌的请求。
本次要介绍的RateLimiter使用的是令牌桶算法。
RateLimiter是google的guava包中的一个轻巧限流组件它主要有两个java类文件RateLimiter.java和SmoothRateLimiter.java。
两个类文件共有java代码301行、注释420行注释比java代码还要多写的非常详细后面的介绍也有相关内容是翻译自其注释有些描述英文原版更加准确清晰有兴趣的也可以结合原版注释进行更详细的了解。
dependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion31.1-jre/version/dependency
示例一、有一系列任务列表要提交执行控制提交速率不超过每秒2个。
此处可能有等待executor.execute(task);}}
{rateLimiter.acquire(packet.length);networkService.send(packet);}
可以看出RateLimiter的使用非常简单只需要构造限速器调用获取许可方法便可不需要释放许可.
许可的发放一般是匀速的但请求未必是匀速的有时会有无请求资源利用不足的场景令牌桶会有贮存机制。
当贮存许可为空时采用透支方式下发新鲜许可同时设置下次许可生效时间为本次新鲜许可的结束时间。
•如下为一个许可发放示例矩形代表整个令牌桶许可产生速度为1个/秒令牌桶里有一个贮存桶容量为2。
以上示例中在T1贮存容量为0许可请求时直接返回1个新鲜许可贮存容量随着时间推移增长至最大值2在T2时收到3个许可的请求此时会先从贮存桶中取出2个然后再产生1个新鲜许可0.5s后在T3时刻又来了1个许可请求由于最近的许可0.5s后才会下发因此先sleep0.5s再下发。
RateLimiter的核心功能是限速我们首先想到的限速方案是记住最后一次下发令牌许可(permit)时间下次许可请求时如果与最后一次下发许可时间的间隔小于1/QPS则进行sleep至1/QPS否则直接发放但该方法不能感知到资源利用不足的场景。
一方面隔了很长一段再来请求许可则可能系统此时相对空闲可下发更多的许可以充分利用资源另一方面隔了很长一段时间再来请求许可也可能意味着处理请求的资源变冷如缓存失效处理效率会下降。
因此在RateLimiter中增加了资源利用不足underutilization的管理在代码中体现为贮存许可(storedPermits)贮存许可值最开始为0随着时间的增加一直增长为最大贮存许可数。
许可获取时首先从贮存许可中获取然后再根据下次新鲜许可获取时间来进行新鲜许可获取。
这里要说的是RateLimiter是记住了下次令牌发放的时间类似于透支的功能当前许可获取时立刻返回同时记录下次获取许可的时间。
RateLimiter类是顶级类也是唯一暴露给使用者的类它提供了工厂方法来创建RateLimiter方法。
warmupPeriod)方法创建的是预热限速器。
同时它提供了acquire方法用于获取令牌提供了tryAcquire方法用于尝试获取令牌。
该类的内部实现上一方面有一个SleepingStopWatch
用于sleep操作另一方面有一个mutexDoNotUseDirectly变量和mutex方法进行互斥加锁。
该类继承了RateLimiter类是一个抽象类含义为平滑限速器限制速率是平滑的maxPermits和storedPermits维护了最大存储许可数量和当前存储许可数量stableIntervalMicros指规定的稳定许可发放间隔nextFreeTicketMicros指下一个空闲许可时间。
平滑突发限速器该类继承了SmoothRateLimiter它存储许可的发放频率同设置的stableIntervalMicros有一个成员变量maxBurstSeconds代表最多存储多长时间的令牌许可。
平滑预热限速器继承了SmoothRateLimiter与SmoothBursty平级它的预热算法需要一定的理解成本。
主体流程主要是对贮存许可数量和新鲜许可数量进行计算和更新得到当前许可请求的等待时间。
SmoothBursty算法和SmoothWarmingUp算法共用这一套主体流程差异主要是贮存许可的管理策略两种算法的不同策略在两个子类中各自实现SmoothBursty算法相对简单一些下面先介绍该算法然后再介绍SmoothWarmingUp算法。
SleepingStopwatch.createFromSystemTimer());}//
*/);rateLimiter.setRate(permitsPerSecond);return
{super(stopwatch);this.maxBurstSeconds
2、rateLimiter.setRate的定义在父类RateLimiter中
{checkArgument(permitsPerSecond
!Double.isNaN(permitsPerSecond),
该方法使用synchronized(mutex())方法对互斥锁进行同步以保证多线程调用的安全然后调用子类的doSetRate方法。
第二个参数nowMicros传的值是调用了stopwatch的方法将限速器创建的时间定义为0然后计算了当前时间和创建时间的时间差因此采用的是相对时间。
该方法使用了双重检查锁来对锁对象mutexDoNotUseDirectly进行懒加载另外该方法通过mutex临时变量来解决了双重检查锁失效的问题。
doSetRate方法的主体实现在SmoothRateLimiter类中
同步贮存许可和时间resync(nowMicros);double
permitsPerSecond;this.stableIntervalMicros
stableIntervalMicros;doSetRate(permitsPerSecond,
该方法在限速器创建时会调用创建后调用限速器的setRate重置速率时也会调用。
resync方法用于基于当前时间刷新计算最新的storedPermis和nextFreeTicketMicros.
coolDownIntervalMicros();storedPermits
newPermits);nextFreeTicketMicros
该方法从现实场景上讲代表的是随着时间的流逝贮存许可不断增加但从技术实现的角度并不是真正的持续刷新而是仅在需要时调用刷新。
该方法如果当前时间小于等于下次许可时间则贮存许可数量和下次许可时间不需要刷新否则通过
(当前时间-下次许可时间)/贮存许可的发放间隔计算出的值域最大贮存数量取小则为已贮存的许可数量需要注意的是贮存许可数量是double类型的。
限速器常用的方法主要有accquire和tryAccquire。
共有两个共有方法一个是无参的每次获取1个许可再一个是整数参数的每次调用获取多个许可。
该方法如果小于等于零则直接返回否则sleepstopwatch.sleepMicrosUninterruptibly(microsToWait);//
从以上源码可看出获取许可的逻辑很简单留出permits个许可根据返回值决定是否sleep等待。
留出许可的方法实现如下
{checkPermits(permits);synchronized
reserveAndGetWaitLength(permits,
reserveEarliestAvailable(permits,
reserveEarliestAvailable为抽象方法实现在SmoothRateLimiter类中该方法是核心主链路方法该方法先从贮存许可中获取如果数量足够则直接返回否则先将全部贮存许可取出再计算还需要的等待时间逻辑如下
刷新贮存许可和下个令牌时间resync(nowMicros);//
等待时间贮存许可等待时间(实现方决定)新鲜许可等待时间(数量*固定速率)long
storedPermitsToWaitTime(this.storedPermits,
透支后的下次许可可用时间当前时间(nextFreeTicketMicros)等待时间(waitMicros)this.nextFreeTicketMicros
LongMath.saturatedAdd(nextFreeTicketMicros,
该方法有两点说明1、returnValue为之前计算的下次空闲时间前面有说RateLimiter采用预支的模式本次直接返回同时计算下次的最早空闲时间
2、贮存许可的等待时间不同的实现方逻辑不同SmoothBursty算法认为贮存许可直接可用所以返回0,
后面的SmoothWarmingUp算法认为贮存许可需要消耗比正常速率更多的预热时间有一定算法逻辑.
至此整个accquire方法的调用链路分析结束下面再看tryAccquire方法就比较简单了tryAccquire比accquire差异的逻辑在于tryAccquire方法会判断下次许可时间-当前时间是否大于超时时间如果是则直接返回false否则进行sleep并返回true.
reserveAndGetWaitLength(permits,
休眠等待stopwatch.sleepMicrosUninterruptibly(microsToWait);return
queryEarliestAvailable(nowMicros)
SmoothWarmingUp算法的主体处理流程同SmoothBurstry算法主要在贮存许可时间计算上的两个方法进行了新实现该算法不像SmoothBurstry算法那么直观好理解需要先了解算法逻辑再看源码。
该算法在源码注释中已经描述的比较清晰了主要思想是限流器的初始贮存许可数量便是最大贮存许可值
贮存许可执行时按一定算法由慢到快的产生直至设定的固定速率以此来达到预热过程。
该算法涉及到一些数学知识如果不是很感兴趣则了解其主要思想便可。
下面详细说一下该算法。
说到该算法前我们再回头看一下SmoothRateLimiter的贮存许可贮存许可有当前数量和最大数量另外还有两个算法逻辑一个是贮存许可生产的速率控制再一个是贮存许可消费速率的控制在Bursty算法中生产的速率同设定的固定速率而消费的速率为无穷大(立刻消费不占用时间在WarmingUp算法中需对照下图进行分析
该图可这样理解每个贮存许可的消费耗时为右侧梯形面积梯形面积(上边长下边长)/2
在限速器初始化时输入的变量有固定速率和预热时间另外冷却因子是固定值3在作者算法中首先计算的是阈值许可数
然后计算的是最大许可数我们知道了梯形的面积、上边(大速率)、下边(小速率)便能推到出高最大许可阀值许可数
stableIntervalMicros;maxPermits
在具体使用中一个是生产的速率固定为预热时间/最大许可数源码如下
再一个是消费的速率按如上曲线从右至左的面积梯形面积长方形面积梯形面积(上边下边)
(availablePermitsAboveThreshold
min(availablePermitsAboveThreshold,
permitsToTime(availablePermitsAboveThreshold)
permitsToTime(availablePermitsAboveThreshold
permitsAboveThresholdToTake);micros
permitsAboveThresholdToTake;}//
coldFactor;SmoothWarmingUp(SleepingStopwatch
{super(stopwatch);this.warmupPeriodMicros
timeUnit.toMicros(warmupPeriod);this.coldFactor
stableIntervalMicros;maxPermits
有storedPermits个贮存许可要使用permitsToTake个时的等待时间计算Overridelong
(availablePermitsAboveThreshold
min(availablePermitsAboveThreshold,
permitsToTime(availablePermitsAboveThreshold)
permitsToTime(availablePermitsAboveThreshold
permitsAboveThresholdToTake);micros
permitsAboveThresholdToTake;}//
冷却间隔固定为预热时间/最大许可数.Overridedouble
RateLimiter内部使用类StopWatch进行了一个相对时间的度量RateLimiter创建时时间为0然后向后累计sleep时不受interrupt异常影响。
RateLimiter暴露的API的许可数量入参为整数类型但内部计算时实际是浮点double类型支持小数许可数量一方面浮点存在丢失精度另一方面也不便于理解是否可以使用整数值得考虑。
RateLimiter的这几种算法只支持单机限流如要支持集群限流一种方式是先根据负载均衡的权重计算出单机的限速值再进行单节点限速另一种方式是参考该组件使用redis等中心化数量管理的中间件但性能和稳定性会降低一些。
RateLimiter提供了有限的扩展能力自带的SmoothBursty和SmoothWarmingUp类不是公开类不能直接创建或调整参数如关闭贮存功能或调整预热系数等。
这种场景需要继承SmoothRateLimiter进行重写贮存许可的生产和消费算法是容易变化和重写的点将整个源码拷贝出来进行二次修改也是一种方案。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback