公司前段时间需要一个限流的功能,使用场景则是在一个滑动时间段内统计某一IP的访问数量
第一次接触窗口的概念还是在学习大数据的时候,在实时处理的时候了解到的,自然而然就想到了窗口统计的操作,不过目前业务量可能还不至于吧,嗯对就是那样
无脑上呗,就利用Redis做了一个实现(不是原子性的)
我太啰嗦了,网上百度一下Redis限流很多理论和操作实践就不罗嗦了,直接上自己魔改过的
基于Redis的ZSet实现的窗口限流操作
忘记参考哪个贴了,如果有相似部分请补充一下原帖链接,代码各位取其精华去其糟粕,嗯~很可能都没了
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
|
public void windowLimit(String key, int[] windowTimes, long[] windowTime, long[] windowLimitTime, TimeUnit timeUnit, String errorMsg) { long currentTime = System.currentTimeMillis(); String windowKey = WINDOW + key; if (redisUtils.exists(WINDOW_LIMIT + key)) { String[] errors = splitError(errorMsg); log.warn("当前[{}]被[{}]限制,锁定时间[{}],错误信息:[{}]", key, "窗口锁", redisUtils.getExpire(WINDOW_LIMIT + key), errors[0]); throw new ErrorCodeException(ErrorCodeEnum.ERROR, errors[1]); } OptionalLong max = Arrays.stream(windowTime).max(); if (max.isPresent()) { long maxWindow = max.getAsLong(); long minScore = currentTime - timeUnit.toMillis(maxWindow); redisUtils.removeRangeByScore(windowKey, 0, minScore - 1); }
for (int i = 0; i < windowTime.length; i++) { long rangeBegin = currentTime - timeUnit.toMillis(windowTime[i]); Set<Object> limits = redisUtils.rangeByScore(windowKey, rangeBegin, currentTime); if (Objects.nonNull(limits)) { if (limits.size() < windowTimes[i]) { continue; } redisUtils.set(WINDOW_LIMIT + key, errorMsg, windowLimitTime[i], timeUnit); if (limits.size() > windowTimes[i]) { String[] errors = splitError(errorMsg); log.warn("当前[{}]被[{}]限制,锁定时间[{}],错误信息:[{}]", key, "窗口锁", redisUtils.getExpire(WINDOW_LIMIT + key), errors[0]); throw new ErrorCodeException(ErrorCodeEnum.ERROR, errors[1]); } } } redisUtils.zSetAdd(windowKey, StringUtils.replaceAll(UUID.randomUUID().toString(), "-", ""), currentTime); }
|
代码写的有点糙请谅解~
还是上面提到的小部分应用还是可以的,大规模的使用还是建议专业的事专业的人干比较好~