常见的限流算法

打印 上一主题 下一主题

主题 810|帖子 810|积分 2430

限流的定义

   限流,也称流量控制。是指系统在面临高并发,或者大流量请求的情况下,限制新的请求对系统的访问,从而包管系统的稳定性。限流会导致部分用户请求处理不实时或者被拒,这就影响了用户体验。以是一样平常必要在系统稳定和用户体验之间均衡一下。
    一样平常对于一些调用必要付费的接口,对用户进行限流利用,限制用户的请求次数
好比:限制单个用户每秒只利用一次
  固定窗口算法

   单元时间内答应部分利用
维护一个计数器,将单元时间段当做一个窗口,计数器记录这个窗口接收请求的次数。
当次数少于限流阀值,就答应访问,并且计数器+1 当次数大于限流阀值,就拒绝访问。
当前的时间窗口已往之后,计数器清零,开始下一个窗口。
  假设单元时间是1秒,限流阀值为3。在单元时间1秒内,每来一个请求,计数器就加1,如果计数器累加的次数超过限流阀值3,后续的请求全部拒绝。等到1s竣事后,计数器清0,重新开始计数
   优点:实现简单
缺点:会出现流量突刺
  

滑动窗口算法

   单元时间内答应部分利用,但这个单元时间是滑动的,必要指定一个滑动单元。
  优点:解决了流量突刺问题
缺点:实现较麻烦,很难找到准确的滑动单元,滑动单元越小,结果越好。
  

漏桶算法(推荐)

   以固定速率处理请求(漏水利用),当请求桶满了之后,就拒绝请求
  

   优点:在一定水平上可以或许应对流量突刺,以固定速率处理请求,可以或许包管服务器的安全
缺点:无法迅速的对请求做出处理,只能一个个按顺序处理(固定速率处理的缺点)
  令牌桶算法(推荐)

   以固定速率向令牌桶添加令牌,每个令牌代表一定数量的请求,请求必要获取令牌之后才可以或许被处理。没有令牌的请求会被拒绝。

    优点:可以或许并发的处理请求,并发的性能会更高一点
缺点:照旧存在时间单元选取的问题
  限流粒度

   1.针对某个方法进行限流,单元时间内最多答应XXX个利用利用利用这个方法。
2,针对某个用户进行限流,某个用户单元时间内最多执行XXX个利用
3.针对某个用户X方法,单个用户单元时间内最多执行XXX次这个方法
  当地限流(单机限流)

   这里采用谷歌的Guava RateLimiter实现
  1. import com.google.common.util.concurrent.RateLimiter;
  2. public static void main(String[] args) {
  3.     // 每秒限流5个请求
  4.     RateLimiter limiter = RateLimiter.create(5.0);
  5.     while (true) {
  6.         if (limiter.tryAcquire()) {
  7.             // 处理请求
  8.         } else {
  9.             // 超过流量限制,需要做何处理
  10.         }
  11.     }
  12. }
复制代码
分布式限流(多机限流)

   如果你的项目有多个服务器,好比微服务,那么发起利用分布式限流。
1.把用户的利用频率等数据放到一个集中的存储进行统计;好比Redis,如许无论用户的
请求落到了哪台服务器,都以集中存储中的数据为准。(Redisson --是一个利用 Redis 的工具库)
2.在网关集中进行限流和统计(好比 SentinelSpring Cloud Gateway)
  1. import org.redisson.Redisson;
  2. import org.redisson.api.RSemaphore;
  3. import org.redisson.api.RedissonClient;
  4. public static void main(String[] args) {
  5.     // 创建RedissonClient
  6.     RedissonClient redisson = Redisson.create();
  7.     // 获取限流器
  8.     RSemaphore semaphore = redisson.getSemaphore("mySemaphore");
  9.     // 尝试获取许可证
  10.     boolean result = semaphore.tryAcquire();
  11.     if (result) {
  12.         // 处理请求
  13.     } else {
  14.         // 超过流量限制,需要做何处理
  15.     }
  16. }
复制代码
分布式限流的实现

   redission配置
  1. package com.cheng.config;
  2. import org.redisson.config.Config;
  3. import lombok.Data;
  4. import org.redisson.Redisson;
  5. import org.redisson.api.RedissonClient;
  6. import org.springframework.boot.context.properties.ConfigurationProperties;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. @Configuration
  10. @ConfigurationProperties(prefix = "spring.redis")
  11. @Data
  12. public class RedissonConfig {
  13.     private String host;
  14.     private Integer port;
  15.     private Integer database;
  16.     private String password;
  17.     @Bean
  18.     public RedissonClient redissonClient() {
  19.         Config config = new Config();
  20.         config.useSingleServer()
  21.         .setAddress("redis://" + host + ":" + port)
  22.         .setDatabase(database)
  23.         .setPassword(password);
  24.         return Redisson.create(config);
  25.     }
  26. }
复制代码
  RedisLimiterManager服务的实现
  1. package com.cheng.manager;
  2. import com.cheng.common.ErrorCode;
  3. import com.cheng.exception.BusinessException;
  4. import org.redisson.api.RRateLimiter;
  5. import org.redisson.api.RateIntervalUnit;
  6. import org.redisson.api.RateType;
  7. import org.redisson.api.RedissonClient;
  8. import org.springframework.stereotype.Service;
  9. import javax.annotation.Resource;
  10. /**
  11. * 专门提供 RedisLimiter 限流基础服务
  12. */
  13. @Service
  14. public class RedisLimiterManager {
  15.     @Resource
  16.     private RedissonClient redissonClient;
  17.     /**
  18.      * 采用令牌桶限流算法
  19.      * 每个用户一个限流器
  20.      *
  21.      * 限流操作
  22.      *
  23.      * @param key 区分不同的限流器,比如不同的用户 id 应该分别统计
  24.      */
  25.     public void doRateLimit(String key) {
  26.         // 创建一个名称为user_limiter的限流器,每秒最多访问 2 次
  27.         RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
  28.         //RateType.OVERALL 统一速率,全局的
  29.         rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);
  30.         // 每当一个操作来了后,请求一个令牌
  31.         boolean canOp = rateLimiter.tryAcquire(1);
  32.         if (!canOp) {
  33.             throw new BusinessException(ErrorCode.REQUEST_OVER);
  34.         }
  35.     }
  36. }
复制代码
  测试类
  1. package com.cheng.springbootinit.manager;
  2. import org.junit.jupiter.api.Test;
  3. import org.springframework.boot.test.context.SpringBootTest;
  4. import javax.annotation.Resource;
  5. import static org.junit.jupiter.api.Assertions.*;
  6. @SpringBootTest
  7. class RedisLimiterManagerTest {
  8.     @Resource
  9.     private RedisLimiterManager redisLimiterManager;
  10.     @Test
  11.     void doRateLimit() throws InterruptedException {
  12.         // 模拟一下操作
  13.         String userId = "1";
  14.         // 瞬间执行2次,每成功一次,就打印'成功'
  15.         for (int i = 0; i < 2; i++) {
  16.             redisLimiterManager.doRateLimit(userId);
  17.             System.out.println("成功");
  18.         }
  19.         // 睡1秒
  20.         Thread.sleep(1000);
  21.         // 瞬间执行5次,每成功一次,就打印'成功'
  22.         for (int i = 0; i < 5; i++) {
  23.             redisLimiterManager.doRateLimit(userId);
  24.             System.out.println("成功");
  25.         }
  26.     }
  27. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曂沅仴駦

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表