在大流量app后端模块设计过程中, 接口限流是必须考虑的事情,本文主要聊聊单机限流的一些常用的实现方案, 以及公司实现的一个 本地限制并发数的Throttling简单实现。
限流的存在的必要性
- 当下互联网出现突发流量带来的挑战
- 防刷
当我们的系统不足以应对当下的流量,那么这个时候我们就需要一种方式阻止流量继续涌入我们的系统,这个时候就需要对用户的请求进行限流
常见限流方案
- 限制并发。
利用全局计数器。比如计数器总共允许一百个。我们在进入方法的时候将这个计数器加1,然后执行完方法将这个计数器-1.等到这个计数器变为0或者负数的时候那么就不能在继续进行访问,这个请求就需要拒绝掉。
限制并发是简单粗暴,但是也就是限制的TPS
- 令牌桶算法
使用一个桶来存放请求需要使用的令牌,令牌桶也就是每秒钟里面只有固定数量的令牌个数。如果令牌没有了那么请求被拒绝或者等待。
常见实现 Throttling:
Throttling是快手基础架构团队实现的一个简单的本地限制并发数的组件, 原理是使用DeQueue&CompletableFuture分别实现了同步限流和异步限流的功能,代码相对比较少,实现的也比较优雅。
关键代码如下:
/**
* 开启 throttling 的perf机制上报,一旦开启,会在perf内埋TL,并在相关组件内读取配置进行相应的打点上报
*/
private static final Kconf<Boolean> ENABLE_THROTTLING_PERF =
ofBoolean("infra.framework.enableThrottlingPerf", false).build();
private static final Kconf<Duration> CLEAN_UNUSED_THROTTLING_DURATION =
ofLong("infra.framework.cleanUnusedThrottlingDurationMs", HOURS.toMillis(1))
.mapper(Duration::ofMillis)
.build();
private static final ThreadLocal<Deque<Throttling>> CURRENT_THROTTLES = ThreadLocal.withInitial(ArrayDeque::new);
private static final Runnable REGISTER_COUNT_PERF = runOnce(() -> {
ExtendableStatsReporter.register("throttlingPerf.totalCount"