网页制作与网站开发...,郑州seo外包公司哪家好,厦门做网站的公司,公司网站系统建设策划书❤ 作者主页#xff1a;李奕赫揍小邰的博客 ❀ 个人介绍#xff1a;大家好#xff0c;我是李奕赫#xff01;(#xffe3;▽#xffe3;)~* #x1f34a; 记得点赞、收藏、评论⭐️⭐️⭐️ #x1f4e3; 认真学习!!!#x1f389;#x1f389; 文章目录 限流的算法漏… ❤ 作者主页李奕赫揍小邰的博客 ❀ 个人介绍大家好我是李奕赫(▽)~* 记得点赞、收藏、评论⭐️⭐️⭐️ 认真学习!!! 文章目录 限流的算法漏桶算法令牌桶算法 限流粒度限流实现构思1.本地限流(单机限流)2.分布式限流(多机限流) Redisson 限流实现1.先引入依赖2.创建 RedissonConfig 配置类用于初始化 RedissonClient 对象单例3.编写 RedisLimiterManager4.在项目引用 最近在写一个项目需要控制用户使用系统的次数以避免超支比如给不同等级的用户分配不同的调用次数防止用户过度使用系统造成破产。现在要做一个解决方案就是限流比如说限制单个用户在每秒只能使用一次那这里我们怎么去思考这个限流的阈值是多少多少合适呢 参考正常用户的使用比如限制单个用户在每秒只能使用 1 次。 限流的算法 限流的算法有很多常用的有两个漏桶算法和令牌桶算法接下来会讲一下这两个算法的用法。
漏桶算法 它的原理很简单可以认为就是注水漏水的过程。往漏桶中以任意速率流入水以固定的速率流出水。当水超过桶的容量时会被溢出也就是被丢弃。因为桶容量是不变的保证了整体的速率。 1.流入的水滴可以看作是访问系统的请求这个流入速率是不确定的。 2.桶的容量一般表示系统所能处理的请求数。 3.如果桶的容量满了就达到限流的阀值就会丢弃水滴拒绝请求 4.流出的水滴是恒定过滤的对应服务按照固定的速率处理请求。 伪代码如下 /*** 每秒处理数出水率*/private long rate;/*** 当前剩余水量*/private long currentWater;/*** 最后刷新时间*/private long refreshTime;/*** 桶容量*/private long capacity;/*** 漏桶算法* return*/boolean leakybucketLimitTryAcquire() {long currentTime System.currentTimeMillis(); //获取系统当前时间long outWater (currentTime - refreshTime) / 1000 * rate; //流出的水量 (当前时间-上次刷新时间)* 出水率long currentWater Math.max(0, currentWater - outWater); // 当前水量 之前的桶内水量-流出的水量refreshTime currentTime; // 刷新时间// 当前剩余水量还是小于桶的容量则请求放行if (currentWater capacity) {currentWater;return true;}// 当前剩余水量大于等于桶的容量限流return false;}没有办法迅速处理一批请求面对突发流量的时候漏桶算法还是循规蹈矩地处理请求按照固定速率处理请求这不是我们想要的结果。
令牌桶算法 面对突发流量的时候我们可以使用令牌桶算法限流。
令牌桶算法原理 1.有一个令牌管理员根据限流大小定速往令牌桶里放令牌。 2.如果令牌数量满了超过令牌桶容量的限制那就丢弃。 3.系统在接受到一个用户请求时都会先去令牌桶要一个令牌。如果拿到令牌那么就处理这个请求的业务逻辑 4.如果拿不到令牌就直接拒绝这个请求。 伪代码如下 /*** 每秒处理数放入令牌数量*/private long putTokenRate;/*** 最后刷新时间*/private long refreshTime;/*** 令牌桶容量*/private long capacity;/*** 当前桶内令牌数*/private long currentToken 0L;/*** 漏桶算法* return*/boolean tokenBucketTryAcquire() {long currentTime System.currentTimeMillis(); //获取系统当前时间long generateToken (currentTime - refreshTime) / 1000 * putTokenRate; //生成的令牌 (当前时间-上次刷新时间)* 放入令牌的速率currentToken Math.min(capacity, generateToken currentToken); // 当前令牌数量 之前的桶内令牌数量放入的令牌数量refreshTime currentTime; // 刷新时间//桶里面还有令牌请求正常处理if (currentToken 0) {currentToken--; //令牌数量-1return true;}return false;}如果令牌发放的策略正确这个系统即不会被拖垮也能提高机器的利用率。Guava的RateLimiter限流组件就是基于令牌桶算法实现的
限流粒度
1.针对某个方法限流即单位时间内最多允许同时 XX 个操作使用这个方法 2.针对某个用户限流比如单个用户单位时间内最多执行 XX 次操作 3.针对某个用户 x 方法限流比如单个用户单位时间内最多执行 XX 次这个方法
限流实现构思
1.本地限流(单机限流) 每个服务器单独限流一般适用于单体项目就是你的项目只有一个服务器 。 在 Java 中有很多第三方库可以用来实现单机限流Guava RateLimiter这是谷歌 Guava 库提供的限流工具可以对单位时间内的请求数量进行限制。 import com.google.common.util.concurrent.RateLimiter;public static void main(String[] args) {// 每秒限流5个请求RateLimiter limiter RateLimiter.create(5.0);while (true) {if (limiter.tryAcquire()) {// 处理请求} else {// 超过流量限制需要做何处理}}
}2.分布式限流(多机限流) 如果你的项目有多个服务器比如微服务那么建议使用分布式限流。 1.把用户的使用频率等数据放到一个集中的存储进行统计 比如 Redis这样无论用户的请求落到了哪台服务器都以集中存储中的数据为准。 (Redisson – 是一个操作 Redis 的工具库) 2.在网关集中进行限流和统计比如 Sentinel、Spring Cloud Gateway
//redission
import org.redisson.Redisson;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;public static void main(String[] args) {// 创建RedissonClientRedissonClient redisson Redisson.create();// 获取限流器RSemaphore semaphore redisson.getSemaphore(mySemaphore);// 尝试获取许可证boolean result semaphore.tryAcquire();if (result) {// 处理请求} else {// 超过流量限制需要做何处理}
}Redisson 限流实现 Redisson 内置了一个限流工具类可以帮助你利用 Redis 来存储、来统计。
1.先引入依赖
dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.21.3/version
/dependency2.创建 RedissonConfig 配置类用于初始化 RedissonClient 对象单例
application.yml文件 RedissonConfig 配置类
Configuration
ConfigurationProperties(prefix spring.redis)
Data
public class RedissonConfig {private Integer database;private String host;private Integer port;private String password;Beanpublic RedissonClient redissonClient() {Config config new Config();config.useSingleServer().setDatabase(database).setAddress(redis:// host : port);RedissonClient redisson Redisson.create(config);return redisson;}
}3.编写 RedisLimiterManager Manager是专门提供 RedisLimiter 限流基础服务的(提供了通用的能力可以放到任何一个项目里)。
Service
public class RedisLimiterManager {Resourceprivate RedissonClient redissonClient;/*** 限流操作** param key 区分不同的限流器比如不同的用户 id 应该分别统计*/public void doRateLimit(String key) {RRateLimiter rateLimiter redissonClient.getRateLimiter(key);// 限流器的统计规则(每秒2个请求;连续的请求,最多只能有2个请求被允许通过)// RateType.OVERALL表示速率限制作用于整个令牌桶,即限制所有请求的速率rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);// 每当一个操作来了后请求一个令牌boolean canOp rateLimiter.tryAcquire(1);// 如果没有令牌,还想执行操作,就抛出异常if (!canOp) {throw new BusinessException(ErrorCode.TOO_MANY_REQUEST);}}
}之后进行一下测试即可。
4.在项目引用
伪代码只需要引入RedisLimiterManager 类直接在需要限流的地方加上即可例如给用户加上限流就是每个用户每秒只允许请求两次
Resource
private RedisLimiterManager redisLimiterManager;User loginUser userService.getLoginUser(request);
// 限流判断每个用户一个限流器
redisLimiterManager.doRateLimit(user_ loginUser.getId());