保障服务稳定的三大利器:熔断降级、服务限流和故障模拟。限流系统是当前很多系统都需要考虑的场景。首先在Nginx层面是可以做限流的,除此之外,在微服务层面还是有很多空间可以施展的。
限流的话,主要思路分为请求数量限制和消费能力限制两类。前者主要是限制一段时间内的总并发数、后者主要是限制消费者的消费能力。
限流算法
限流算法来说,主要包含令牌桶算法、漏桶算法和计数器等。对于简单的计数器算法,通过AtomicLong#incrementAndGet()来进行粗暴的控制,因为容易导致“突刺现象”(比如单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能拒绝),所以这里不做推荐。
令牌桶算法
令牌工厂:匀速生成令牌
令牌桶:拥有固定的令牌数
应用者:一次可以申请N个令牌,没有令牌不能进行后续处理。
如果使用Redis来实现的话会比较简单,大概思路如下:
1/ 获取令牌:依靠List的leftPop来获取令牌
Object result = redisTemplate.opsForList().leftPop("limit_list");
2/ 向令牌桶添加令牌
redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());
漏桶算法
漏桶:容量固定
流速:任意
流出水滴:固定速率
滑动窗口
用Redis的list数据结构可以轻而易举的实现该功能:保证每N秒内至多M个请求,缺点就是zset的数据结构会越来越大。实现方式相对也是比较简单的。
1/ 请求进来:UUID生成唯一的value;score用当前的时间戳
redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime);
2/ 限流:zset的range方法可以统计两个时间戳内的请求,达到限流效果
Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime -