关于高并发限流那些事

面试被问到限流算法这块,这里总结下关于限流的算法和方案。计数器算法、滑动窗口算法、漏桶算法、令牌桶算法我的极简笔记。java

为何须要限流

正常的业务量增加不是瞬时的,能够采用应用实例或者数据库实例的垂直或水平伸缩应对,而限流针对场景主要两种:nginx

  1. 网络攻击、爬虫程序面试

  2. 热点事件触发业务(如各平台营销活动、微博热点话题)redis

限流算法

一、计数器算法

原理:请求达到时计数器+1,而后比较当前计数是否达到阈值。算法

具体有两种实现:数据库

1.1 判断系统正在处理的请求数是否达到阈值网络

请求处理前计数器+1,请求处理完成计数器-1,好比要控制系统同时处理的请求不超过100个。并发

static AtomicInteger runningThread = new AtomicInteger(0);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    if (runningThread.get() > MAX_VALUE) {
    return getVoidMono(exchange, BaseResult.builder().code("SYSTEM_BUSY").message("系统繁忙").build());
    }
    runningThread.getAndIncrement();
    Mono<Void> mono = chain.filter(exchange)
    .then(Mono.fromRunnable(() -> runningThread.getAndDecrement()));
    return mono;
}

1.2  判断单位时间内请求数是否达到阈值运维

如:每分钟处理请求不超过100个。ide

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    //redis键值自增,key不存在时自动建立,初始值为0
    Long v = redisTemplate.opsForValue().increment(key, 1L);
    //初始化生效时间
    if (v == 1) {
    redisTemplate.expire(key, 1L, TimeUnit.MINUTES);
    }
    //请求数大于阈值拦截处理
    if (v > MAX_VALUE) {
    return getVoidMono(exchange, BaseResult.builder().code("SYSTEM_BUSY").message("系统繁忙").build());
    }
    return chain.filter(exchange);
}

这种限流方法有明显的缺陷,以下图,分别看两个单位时间内的并发数是被限制到每分钟100个请求了,可是若是请求若是集中分布在第一分钟的最后一秒和第二分钟的第一秒之间,算出的并发成了每秒100个请求。

二、滑动窗口算法

滑动窗口本质上仍是计数器算法,只是采用了更加细粒度的计数,对滚动区间进行计数。针对上述问题,将1分钟拆分为10个格子即每10秒一次滑动,统计当前一分钟窗口内的总计数。针对上述的问题,把一分钟拆成60个格子,即每秒一次滑动。00:59秒进入100个请求,01:00的请求则被限流拦截。

so 滑动窗口的本质是对粗粒度限流必定程度的优化,假如一开始就用极细粒度时间间隔作计数统计,也能实现较为精确的限流,但同时由于频繁的进行计数器的重置牺牲部分效率。

三、 漏桶算法

原理:请求排队,系统匀速取出排队请求进行处理。

漏桶很形象,无论进水速度如何,漏孔滴水的速率是匀速稳定的。

四、令牌桶算法

划重点,目前应用比较普遍的算法。

原理: 好比每分钟/秒产生必定数目的令牌到令牌桶中(令牌桶满则忽略),请求到达时网关获取令牌成功则处理,失败则触发限流逻辑。

Google开源工具包Guava提供了基于令牌桶算法的限流工具类RateLimiter。

详细用法 :https://cloud.tencent.com/developer/article/1408819

//每秒生成10个令牌,5秒预热时间。
static RateLimiter rateLimiter = RateLimiter.create(10, 5, TimeUnit.SECONDS);

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    if (!rateLimiter.tryAcquire()) {
    return getVoidMono(exchange, BaseResult.builder().code("SYSTEM_BUSY").message("系统繁忙").build());
    }
    return chain.filter(exchange);
}

总结限流实现方案

一、 网关代码层限流,自定义限流逻辑,可以使用
Semaphore、AtomicInteger、Redis等计数器
RateLimiter(令牌桶)
RedisRateLimter(可根据IP、用户、接口路径等定制限流规则)

二、运维代理层
nginx配置:限制并发链接数、根据IP、接口限制并发访问速率。


本文持续完善中....

相关文章
相关标签/搜索