From 7aca97aa2e600bf74266c9e33f9b6470752d19be Mon Sep 17 00:00:00 2001 From: ehlxr Date: Thu, 15 Jul 2021 21:46:50 +0800 Subject: [PATCH 1/4] update at 2021-07-15 21:46:50 by ehlxr --- .../io/github/ehlxr/rate/SlideWindow.java | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/main/java/io/github/ehlxr/rate/SlideWindow.java diff --git a/src/main/java/io/github/ehlxr/rate/SlideWindow.java b/src/main/java/io/github/ehlxr/rate/SlideWindow.java new file mode 100644 index 0000000..1c8b208 --- /dev/null +++ b/src/main/java/io/github/ehlxr/rate/SlideWindow.java @@ -0,0 +1,107 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2020 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.rate; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +/** + * 滑动时间窗口限流工具 + * 本限流工具只适用于单机版,如果想要做全局限流,可以按本程序的思想,用redis的List结构去实现 + * https://www.cnblogs.com/dijia478/p/13807826.html#!comments + * + * @author dijia478 + * @date 2020-10-13 10:53 + */ +public class SlideWindow { + /** + * 限制次数 + */ + private final int count; + + /** + * 时间窗口大小(毫秒) + */ + private final long timeWindow; + + /** + * 限流队列 + */ + private final List list; + + public SlideWindow(int count, long timeWindow) { + this.count = count; + this.timeWindow = timeWindow; + + list = new LinkedList<>(); + } + + /** + * 滑动时间窗口限流算法 + * 在指定时间窗口,指定限制次数内,是否允许通过 + * + * @return 是否允许通过 + */ + public synchronized boolean isGo() { + // 获取当前时间 + long nowTime = System.currentTimeMillis(); + // 如果队列还没满,则允许通过,并添加当前时间戳到队列开始位置 + if (list.size() < count) { + // 把之前这个位置的数据给依次向后移动 + list.add(0, nowTime); + return true; + } + + // 队列已满(达到限制次数),则获取队列中最早添加的时间戳 + // 用当前时间戳 减去 最早添加的时间戳 + if (nowTime - list.get(count - 1) <= timeWindow) { + // 若结果小于等于timeWindow,则说明在timeWindow内,通过的次数大于count + // 不允许通过 + return false; + } else { + // 若结果大于timeWindow,则说明在timeWindow内,通过的次数小于等于count + // 允许通过,并删除最早添加的时间戳,将当前时间添加到队列开始位置 + list.remove(count - 1); + list.add(0, nowTime); + return true; + } + } + + public static void main(String[] args) throws InterruptedException { + SlideWindow slideWindow = new SlideWindow(5, 1000); + + while (true) { + // 任意1秒内,只允许5次通过 + if (slideWindow.isGo()) { + System.out.println(System.currentTimeMillis()); + } + // 睡眠0-10ms + Thread.sleep(new Random().nextInt(10)); + } + + } + +} \ No newline at end of file From e106affde0627e8feaa8e4c28a29af4a87a2bb72 Mon Sep 17 00:00:00 2001 From: ehlxr Date: Thu, 15 Jul 2021 22:04:29 +0800 Subject: [PATCH 2/4] add SlideWindow --- .../io/github/ehlxr/rate/SlideWindow.java | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/ehlxr/rate/SlideWindow.java b/src/main/java/io/github/ehlxr/rate/SlideWindow.java index 1c8b208..36184b9 100644 --- a/src/main/java/io/github/ehlxr/rate/SlideWindow.java +++ b/src/main/java/io/github/ehlxr/rate/SlideWindow.java @@ -27,6 +27,7 @@ package io.github.ehlxr.rate; import java.util.LinkedList; import java.util.List; import java.util.Random; +import java.util.concurrent.TimeUnit; /** * 滑动时间窗口限流工具 @@ -65,7 +66,7 @@ public class SlideWindow { * * @return 是否允许通过 */ - public synchronized boolean isGo() { + public synchronized boolean acquire() { // 获取当前时间 long nowTime = System.currentTimeMillis(); // 如果队列还没满,则允许通过,并添加当前时间戳到队列开始位置 @@ -90,14 +91,36 @@ public class SlideWindow { } } + public synchronized void tryAcquire() throws InterruptedException { + long nowTime = System.currentTimeMillis(); + if (list.size() < count) { + list.add(0, nowTime); + return; + } + + long l = nowTime - list.get(count - 1); + if (l <= timeWindow) { + // 等待 + TimeUnit.MILLISECONDS.sleep(timeWindow - l); + tryAcquire(); + } else { + list.remove(count - 1); + list.add(0, nowTime); + } + } + public static void main(String[] args) throws InterruptedException { SlideWindow slideWindow = new SlideWindow(5, 1000); while (true) { // 任意1秒内,只允许5次通过 - if (slideWindow.isGo()) { - System.out.println(System.currentTimeMillis()); - } + // if (slideWindow.acquire()) { + // System.out.println(System.currentTimeMillis()); + // } + slideWindow.tryAcquire(); + + System.out.println(System.currentTimeMillis()); + // 睡眠0-10ms Thread.sleep(new Random().nextInt(10)); } From cc013f3b8672bfebd44fb964dc9189cb5accb56c Mon Sep 17 00:00:00 2001 From: ehlxr Date: Thu, 15 Jul 2021 22:05:54 +0800 Subject: [PATCH 3/4] add SlideWindow --- src/main/java/io/github/ehlxr/rate/SlideWindow.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/ehlxr/rate/SlideWindow.java b/src/main/java/io/github/ehlxr/rate/SlideWindow.java index 36184b9..ea2e7bd 100644 --- a/src/main/java/io/github/ehlxr/rate/SlideWindow.java +++ b/src/main/java/io/github/ehlxr/rate/SlideWindow.java @@ -31,11 +31,10 @@ import java.util.concurrent.TimeUnit; /** * 滑动时间窗口限流工具 - * 本限流工具只适用于单机版,如果想要做全局限流,可以按本程序的思想,用redis的List结构去实现 - * https://www.cnblogs.com/dijia478/p/13807826.html#!comments + * 参考:https://www.cnblogs.com/dijia478/p/13807826.html#!comments * - * @author dijia478 - * @date 2020-10-13 10:53 + * @author ehlxr + * @since 2021-07-15 22:04. */ public class SlideWindow { /** @@ -91,6 +90,9 @@ public class SlideWindow { } } + /** + * 等待直到获得允许 + */ public synchronized void tryAcquire() throws InterruptedException { long nowTime = System.currentTimeMillis(); if (list.size() < count) { From d3b548c8b4e69c71b8b74c346d32092b07e3729a Mon Sep 17 00:00:00 2001 From: ehlxr Date: Thu, 15 Jul 2021 23:01:08 +0800 Subject: [PATCH 4/4] add SlideWindow --- pom.xml | 8 +- .../java/io/github/ehlxr/redis/RedisDAO.java | 120 ++++++++++++++++ .../github/ehlxr/redis/impl/JedisDAOImpl.java | 129 ++++++++++++++++++ .../ehlxr/redis/impl/LettuceDAOImpl.java | 114 ++++++++++++++++ 4 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/github/ehlxr/redis/RedisDAO.java create mode 100644 src/main/java/io/github/ehlxr/redis/impl/JedisDAOImpl.java create mode 100644 src/main/java/io/github/ehlxr/redis/impl/LettuceDAOImpl.java diff --git a/pom.xml b/pom.xml index d42614a..69c9a07 100644 --- a/pom.xml +++ b/pom.xml @@ -173,7 +173,13 @@ redis.clients jedis - 2.8.0 + 3.4.0 + + + + org.springframework.boot + spring-boot-starter-data-redis + 2.5.2 diff --git a/src/main/java/io/github/ehlxr/redis/RedisDAO.java b/src/main/java/io/github/ehlxr/redis/RedisDAO.java new file mode 100644 index 0000000..7f73809 --- /dev/null +++ b/src/main/java/io/github/ehlxr/redis/RedisDAO.java @@ -0,0 +1,120 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2020 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.redis; + +/** + * redis lock/滑动时间窗口限流 + * + * @author ehlxr + * @since 2021-07-15 22:42. + */ +public interface RedisDAO { + + /** + * 释放分布式锁时使用的 lua 脚本,保证原子性 + *

+ * if (redis.call('get', KEYS[1]) == ARGV[1]) + * then + * return redis.call('del', KEYS[1]) + * else + * return 0 + * end + */ + String RELEASE_LOCK_LUA = "if (redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end"; + + /** + * 滑动窗口限流使用的 lua 脚本,保证原子性 + *

+ * local key = KEYS[1]; + * local index = tonumber(ARGV[1]); + * local time_window = tonumber(ARGV[2]); + * local now_time = tonumber(ARGV[3]); + * local far_time = redis.call('lindex', key, index); + * if (not far_time) + * then + * redis.call('lpush', key, now_time); + * redis.call('pexpire', key, time_window+1000); + * return 1; + * end + * if (now_time - far_time > time_window) + * then + * redis.call('rpop', key); + * redis.call('lpush', key, now_time); + * redis.call('pexpire', key, time_window+1000); + * return 1; + * else + * return 0; + * end + */ + String SLIDE_WINDOW_LUA = "local key = KEYS[1];\n" + "local index = tonumber(ARGV[1]);\n" + "local time_window = tonumber(ARGV[2]);\n" + "local now_time = tonumber(ARGV[3]);\n" + "local far_time = redis.call('lindex', key, index);\n" + "if (not far_time)\n" + "then\n" + " redis.call('lpush', key, now_time);\n" + " redis.call('pexpire', key, time_window+1000);\n" + " return 1;\n" + "end\n" + "\n" + "if (now_time - far_time > time_window)\n" + "then\n" + " redis.call('rpop', key);\n" + " redis.call('lpush', key, now_time);\n" + " redis.call('pexpire', key, time_window+1000);\n" + " return 1;\n" + "else\n" + " return 0;\n" + "end"; + + /** + * 获取分布式锁 + * + * @param logId 日志 id + * @param key key + * @param value value,需要保证全局唯一,用来删除分布式锁时判断身份使用 + * @param expireTime 锁过期时间,毫秒,防止业务崩溃未删除锁,导致死锁 + * @return 是否获取成功锁 + */ + Boolean getDistributedLock(String key, String value, long expireTime); + + /** + * 释放分布式锁 + * + * @param logId 日志 id + * @param key key + * @param value value,需要和获取锁时传入的一致 + * @return 是否释放成功锁 + */ + Boolean releaseDistributedLock(String key, String value); + + /** + * 分布式限流队列,在时间窗口内(包含该时间点),判断是否达到限流的阀值 + * 本接口实现的方法通过加锁避免并发问题,性能不高。只是为了说明限流逻辑如何实现 + * + * @param logId 日志 id + * @param key key + * @param count 限流阀值 + * @param timeWindow 限流时间窗口 + * @return 是否允许通过(通过即不进行限流) + */ + Boolean slideWindow(String key, int count, long timeWindow); + + /** + * 分布式限流队列,在时间窗口内(包含该时间点),判断是否达到限流的阀值 + * 本接口实现的方法通过 Lua 脚本避免并发问题,性能较高。 + * + * @param logId 日志 id + * @param key key + * @param count 限流阀值 + * @param timeWindow 限流时间窗口 + * @return 是否允许通过(通过即不进行限流) + */ + Boolean slideWindowLua(String key, int count, long timeWindow); + +} + + diff --git a/src/main/java/io/github/ehlxr/redis/impl/JedisDAOImpl.java b/src/main/java/io/github/ehlxr/redis/impl/JedisDAOImpl.java new file mode 100644 index 0000000..5a6249f --- /dev/null +++ b/src/main/java/io/github/ehlxr/redis/impl/JedisDAOImpl.java @@ -0,0 +1,129 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2020 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.redis.impl; + +import io.github.ehlxr.redis.RedisDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import redis.clients.jedis.JedisCluster; +import redis.clients.jedis.params.SetParams; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * redis lock/滑动时间窗口限流 + * + * @author ehlxr + * @since 2021-07-15 22:44. + */ +public class JedisDAOImpl implements RedisDAO { + private final Logger log = LoggerFactory.getLogger(JedisDAOImpl.class); + + @Autowired + private JedisCluster jc; + + @Override + public Boolean getDistributedLock(String key, String value, long expireTime) { + String set = null; + try { + set = jc.set(key, value, SetParams.setParams().nx().px(expireTime)); + log.debug("getLock redis key: {}, value: {}, expireTime: {}, result: {}", key, value, expireTime, set); + } catch (Exception e) { + log.error("getLock redis key: {}, value: {}, expireTime: {}", key, value, expireTime, e); + } + return "OK".equals(set); + } + + @Override + public Boolean releaseDistributedLock(String key, String value) { + Object eval = null; + try { + eval = jc.eval(RELEASE_LOCK_LUA, Collections.singletonList(key), Collections.singletonList(value)); + log.debug("releaseLock redis key: {}, value: {}, result: {}", key, value, eval); + } catch (Exception e) { + log.error("releaseLock redis key: {}, value: {}", key, value, e); + } + return Long.valueOf(1L).equals(eval); + } + + @Override + public synchronized Boolean slideWindow(String key, int count, long timeWindow) { + if (count <= 0 || timeWindow <= 0) { + return false; + } + try { + // 获取当前时间 + long nowTime = System.currentTimeMillis(); + // 获取队列中,达到限流数量的位置,存储的时间戳 + String farTime = jc.lindex(key, count - 1); + // 如果为空,说明限流队列还没满,则允许通过,并添加当前时间戳到队列开始位置 + if (farTime == null) { + jc.lpush(key, String.valueOf(nowTime)); + // 给限流队列增加过期时间,防止长时间不用导致内存一直占用 + jc.pexpire(key, timeWindow + 1000L); + return true; + } + + // 队列已满(达到限制次数),用当前时间戳 减去 最早添加的时间戳 + if (nowTime - Long.parseLong(farTime) > timeWindow) { + // 若结果大于 timeWindow,则说明在 timeWindow 内,通过的次数小于等于 count + // 允许通过,并删除最早添加的时间戳,将当前时间添加到队列开始位置 + jc.rpop(key); + jc.lpush(key, String.valueOf(nowTime)); + // 给限流队列增加过期时间,防止长时间不用导致内存一直占用 + jc.pexpire(key, timeWindow + 1000L); + return true; + } + // 若结果小于等于 timeWindow,则说明在 timeWindow 内,通过的次数大于 count + // 不允许通过 + return false; + } catch (Exception e) { + log.error("[logId:{}]", e); + return false; + } + } + + @Override + public Boolean slideWindowLua(String key, int count, long timeWindow) { + if (count <= 0 || timeWindow <= 0) { + return false; + } + Object eval = null; + try { + List argvList = new ArrayList<>(); + argvList.add(String.valueOf(count - 1)); + argvList.add(String.valueOf(timeWindow)); + argvList.add(String.valueOf(System.currentTimeMillis())); + eval = jc.eval(SLIDE_WINDOW_LUA, Collections.singletonList(key), argvList); + log.debug("slideWindowLua redis key: {}, count: {}, timeWindow: {}, result: {}", key, count, timeWindow, eval); + } catch (Exception e) { + log.error("slideWindowLua redis key: {}, count: {}, timeWindow: {}", key, count, timeWindow, e); + } + return Long.valueOf(1L).equals(eval); + } +} diff --git a/src/main/java/io/github/ehlxr/redis/impl/LettuceDAOImpl.java b/src/main/java/io/github/ehlxr/redis/impl/LettuceDAOImpl.java new file mode 100644 index 0000000..bf5f5b6 --- /dev/null +++ b/src/main/java/io/github/ehlxr/redis/impl/LettuceDAOImpl.java @@ -0,0 +1,114 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2020 xrv + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package io.github.ehlxr.redis.impl; + +import io.github.ehlxr.redis.RedisDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.ListOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +/** + * @author ehlxr + * @since 2021-07-15 22:51. + */ +public class LettuceDAOImpl implements RedisDAO { + private final Logger log = LoggerFactory.getLogger(LettuceDAOImpl.class); + + @Autowired + private RedisTemplate rt; + + @Override + public Boolean getDistributedLock(String key, String value, long expireTime) { + Boolean set = false; + try { + set = rt.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS); + log.debug("getLock redis key: {}, value: {}, expireTime: {}, result: {}", key, value, expireTime, set); + } catch (Exception e) { + log.error("getLock redis key: {}, value: {}, expireTime: {}", key, value, expireTime, e); + } + return set; + } + + @Override + public Boolean releaseDistributedLock(String key, String value) { + Long execute = null; + try { + RedisScript redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA, Long.class); + execute = rt.execute(redisScript, Collections.singletonList(key), value); + log.debug("releaseLock redis key: {}, value: {}, result: {}", key, value, execute); + } catch (Exception e) { + log.error("releaseLock redis key: {}, value: {}", key, value, e); + } + return Long.valueOf(1L).equals(execute); + } + + @Override + public synchronized Boolean slideWindow(String key, int count, long timeWindow) { + try { + long nowTime = System.currentTimeMillis(); + ListOperations list = rt.opsForList(); + String farTime = list.index(key, count - 1); + if (farTime == null) { + list.leftPush(key, String.valueOf(nowTime)); + rt.expire(key, timeWindow + 1000L, TimeUnit.MILLISECONDS); + return true; + } + if (nowTime - Long.parseLong(farTime) > timeWindow) { + list.rightPop(key); + list.leftPush(key, String.valueOf(nowTime)); + rt.expire(key, timeWindow + 1000L, TimeUnit.MILLISECONDS); + return true; + } + return false; + } catch (Exception e) { + log.error("[logId:{}]", e); + return false; + } + } + + @Override + public Boolean slideWindowLua(String key, int count, long timeWindow) { + if (count <= 0 || timeWindow <= 0) { + return false; + } + Long execute = null; + try { + RedisScript redisScript = new DefaultRedisScript<>(SLIDE_WINDOW_LUA, Long.class); + execute = rt.execute(redisScript, Collections.singletonList(key), String.valueOf(count - 1), String.valueOf(timeWindow), String.valueOf(System.currentTimeMillis())); + log.debug("slideWindowLua redis key: {}, count: {}, timeWindow: {}, result: {}", key, count, timeWindow, execute); + } catch (Exception e) { + log.error("slideWindowLua redis key: {}, count: {}, timeWindow: {}", key, count, timeWindow, e); + } + return Long.valueOf(1L).equals(execute); + } + +}