赞
踩
限流是在高并发或者某个瞬间高并发时,为了保证系统的稳定性,对超出服务处理能力之外的请求进行拦截,对访问服务的流量进行限制。
常见的限流算法有四种:固定窗口限流算法、滑动窗口限流算法、漏桶限流算法和令牌桶限流算法。
将每个固定时间设置为每个固定窗口,每个固定窗口只能处理固定数量的请求,如果这个窗口处理了限定的请求数量,之后的请求都不再处理,直到下一个时间窗口。固定窗口限流实现简单,但是会出现流量突刺的问题,假设窗口为1s,请求数量为100,999ms 之前都没有请求来,999ms 的时候来了100个请求都接收了,此时下一个窗口的第1ms 的时候也来了100个请求,这就导致系统在短短2ms 内要处理200个请求,无疑会加重系统负载。
打个比方,有一个面馆,每个小时只能20人吃面,划分9点到10点为一个窗口,10点到11点为一个窗口,假设9点59分59秒来了20人吃面,10点00分01秒来了20人吃面,这就导致两秒钟面馆要做40碗面,压力剧增。
固定窗口是将时间划分为多个窗口,比如每一小时都是一个固定窗口,9点到10点是一个窗口,10点到11点是一个窗口,每个窗口随着时间变化逐渐变小,所以会出现两个流量突刺的问题。
滑动窗口是将一个小时作为窗口,窗口的大小始终不变,在这个窗口内只能存在20个请求。
打个比方,还是那个面馆,这个面馆是一个滑动窗口,面馆里有20个位置,最大只能容纳20个人同时吃面。假如出现上面的情况,9点59分59秒来了20人,10点00分01秒来了20人,虽然来了40人,但是店里只能容纳20人吃面。后来的20人只能等着或者被拒绝。
系统处理请求就像是一个往一个漏桶中装水,如果装的水量很大,下面的孔来不及流出去,水就会溢出来。保证系统始终以稳定的速率处理请求,不会出现压力激增的问题。
但是因为始终以恒定的速率处理请求,如果系统有多余的资源,效率比较慢。
在漏桶的基础上,加上了令牌的概念,以恒定的速率往桶里加令牌,请求到来的时候先获取令牌,然后被处理,如果处理请求的速率慢于加令牌的速度,桶内始终会有令牌,那么请求来了就能被处理。如果处理速率快于令牌,那么桶里会出现没有令牌的情况,到来的请求就会被拒绝。
打个比方,流水线始终以固定的速率生产配件,配件即为令牌,生产的配件会被放在仓库,仓库就是桶,工厂接到了订单请求会先确认仓库中,如果有配件,就会处理订单,如果没配件了,订单就会被拒绝。
github地址:
实现主要参考开源地址
1. 配置依赖
- <dependency>
- <groupId>org.redisson</groupId>
- <artifactId>redisson</artifactId>
- <version>3.30.0</version>
- </dependency>
2. 创建Redisson配置类
- /**
- * Redisson 配置
- */
- @Configuration
- @EnableCaching
- @ConfigurationProperties(prefix = "spring.redis")
- @Data
- public class RedissonConfig {
-
- private String port;
-
- private String host;
-
- private Integer database;
-
- private Integer timeout;
- @Bean(destroyMethod = "shutdown")
- public RedissonClient redissonClient(){
- // 1. 创建配置
- Config config = new Config();
- String redisAddress = String.format("redis://%s:%S",host,port);
- //设置redis地址,redis 库,过期时间
- config.useSingleServer().setAddress(redisAddress).setDatabase(database).setTimeout(timeout);
- // 2. 创建Redisson实例
- // Sync and Async API
- return Redisson.create(config);
- }
- }

3. 创建限流管理器
- @Service
- public class RedisLimiterManager {
- @Resource
- private RedissonClient redissonClient;
-
- /**
- * 限流,令牌桶算法
- * @param key 区分不同的限流器,这里是对应的redis的key
- */
- public void doRateLimit(String key){
- RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
- //每秒执行两个请求,设置速率
- rateLimiter.trySetRate(RateType.OVERALL,2,1, RateIntervalUnit.SECONDS);
- //每个请求到来请求 1 个令牌
- boolean b = rateLimiter.tryAcquire(1);
- if(!b){
- throw new BusinessException(ErrorCode.SYSTEM_ERROR,"请求频繁,请稍后再试");
- }
- }
- }

4. 调用限流管理器即可
- //注入管理器
- @Resource
- private RedisLimiterManager redisLimiterManager;
-
- //调用
- //限流每个用户调用方法的次数
- redisLimiterManager.doRateLimit("genCharByAi:"+userId);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。