limiter限流 单机限流 思路介绍 采用 Google 的 Guava 中的 RateLimiter.create 来进行令牌桶的分发,但是我们需要动态控制限流的速率,所以在这上面利用单例模式的“ 双重检查锁 ”和 “懒汉式”来进行动态创建
1 2 3 4 5 <dependency > <groupId > com.google.guava</groupId > <artifactId > guava</artifactId > <version > 20.0</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.tthk.inland.ticket.core.utils.limiters;import com.google.common.util.concurrent.RateLimiter;import lombok.extern.slf4j.Slf4j;import java.util.Optional;@Slf4j public class GuavaRateLimiterUtils { private final static Integer DEFAULT_RATE = 80 ; private static RateLimiter instance = null ; private GuavaRateLimiterUtils () {} public static boolean initRateLimiter (Integer rate) { try { Integer limiter = Optional.ofNullable(rate).orElse(DEFAULT_RATE); if (instance == null || limiter.equals(DEFAULT_RATE)){ synchronized (GuavaRateLimiterUtils.class){ if (instance == null || limiter.equals(DEFAULT_RATE)){ instance = RateLimiter.create(limiter); } } } if (!instance.tryAcquire(1 )){ log.info("限流中" ); return true ; } }catch (Exception e){ log.info(e.getMessage()); } return false ; } }
分布式限流 思路介绍 采用 Redis 队列实现滑动窗口的方式来进行限流,利用 Redis 的 SETNX 特性来加分布式锁,保证执行顺序
1 2 3 4 5 6 <dependency > <groupId > org.redisson</groupId > <artifactId > redisson</artifactId > <version > 2.9.3</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 package com.tthk.inland.ticket.core.utils.limiters;import lombok.extern.slf4j.Slf4j;import org.redisson.Redisson;import org.redisson.api.RLock;import org.redisson.api.RedissonClient;import org.redisson.config.Config;import java.util.concurrent.TimeUnit;@Slf4j public class RedisLinkUtils { private static volatile RedissonClient redissonClient; private static final String URL = "" ; private static final String PORT = "" ; private static final String PASSWORD = "" ; private static final Integer DB = 17 ; public final static String TASK_NAME = "REDIS_LIMIT" ; public final static String TASK_LOCK = "REDIS_LOCK" ; public static RedissonClient getRedissonClient () { if (redissonClient == null ){ synchronized (RedisLinkUtils.class){ if (redissonClient == null ){ Config config = new Config (); config.useSingleServer().setAddress("redis://" +URL+":" +PORT) .setPassword(PASSWORD) .setDatabase(DB); redissonClient = Redisson.create(config); redissonClient.getBlockingDeque(TASK_NAME).clear(); } } } return redissonClient; } public static boolean acquireSecond (Integer waitTime, Integer leaseTime) { try { RLock myLock = redissonClient.getLock(TASK_LOCK); return myLock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); } catch (InterruptedException e) { log.info(e.getMessage()); } return false ; } public static void release () { if (redissonClient != null ) { RLock myLock = redissonClient.getLock(TASK_LOCK); if (myLock != null ){ myLock.unlock(); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 package com.tthk.inland.ticket.core.utils.limiters;import lombok.extern.slf4j.Slf4j;import java.util.*;@Slf4j public class RedisRateLimiterUtils { private final static Integer DEFAULT_RATE = 80 ; private final static Long timeout = 1L ; public static Boolean initRateLimiter (Integer rate) { Long currentTimeMillis = System.currentTimeMillis(); Integer limiter = Optional.ofNullable(rate).orElse(DEFAULT_RATE); Deque<Long> stack = RedisLinkUtils.getRedissonClient().getBlockingDeque(RedisLinkUtils.TASK_NAME); try { if (stack.size() >= limiter){ if (RedisLinkUtils.acquireSecond(1 ,2 )){ Long firstTime = Optional.ofNullable(stack.peekFirst()).orElse(currentTimeMillis); if ((currentTimeMillis - firstTime) < (timeout * 1000 )){ log.info("限流中" ); return true ; } while (stack.size() >= limiter){ stack.pollFirst(); } RedisLinkUtils.release(); }else { log.info("未获取到分布式锁,限流!" ); return true ; } } }catch (Exception e){ log.info(e.getMessage()); } RedisLinkUtils.getRedissonClient().getBlockingDeque(RedisLinkUtils.TASK_NAME).offerLast(currentTimeMillis); return false ; } public static void main (String[] args) throws InterruptedException { for (int i = 0 ; i < 100 ; i++) { while (initRateLimiter(1 )){ Thread.sleep(300 ); }; System.out.println(i+"次" ); } } }