| | |
| | | |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * 仅当key不存在时写入,并设置过期时间。 |
| | | * |
| | | * @param key key |
| | | * @param value value |
| | | * @param time 过期时间,单位:秒 |
| | | * @return 是否写入成功 |
| | | */ |
| | | public boolean setIfAbsent(String key, String value, int time) { |
| | | if (StringUtil.isEmpty(key)) { |
| | | return false; |
| | | } |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | return "OK".equals(resource.set(key, value, "NX", "EX", time)); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 原子比较并删除。key存在时无论是否匹配都会被删除,避免验证码被暴力枚举。 |
| | | */ |
| | | public boolean consumeIfEquals(String key, String expectedValue) { |
| | | if (StringUtil.isEmpty(key) || expectedValue == null) { |
| | | return false; |
| | | } |
| | | String script = "local value = redis.call('get', KEYS[1]); " |
| | | + "if not value then return 0 end; " |
| | | + "redis.call('del', KEYS[1]); " |
| | | + "if value == ARGV[1] then return 1 else return 0 end"; |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | Object result = resource.eval( |
| | | script, |
| | | Collections.singletonList(key), |
| | | Collections.singletonList(expectedValue) |
| | | ); |
| | | return Long.valueOf(1L).equals(result); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取固定时间窗口内的一个名额。 |
| | | */ |
| | | public boolean tryAcquireFixedWindow(String key, int maxCount, int windowSeconds) { |
| | | if (StringUtil.isEmpty(key) || maxCount <= 0 || windowSeconds <= 0) { |
| | | return false; |
| | | } |
| | | String script = "local current = tonumber(redis.call('get', KEYS[1]) or '0'); " |
| | | + "if current >= tonumber(ARGV[2]) then return 0 end; " |
| | | + "current = redis.call('incr', KEYS[1]); " |
| | | + "if current == 1 then redis.call('expire', KEYS[1], ARGV[1]) end; return 1"; |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | Object result = resource.eval( |
| | | script, |
| | | Collections.singletonList(key), |
| | | Arrays.asList(String.valueOf(windowSeconds), String.valueOf(maxCount)) |
| | | ); |
| | | return Long.valueOf(1L).equals(result); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 释放固定时间窗口内已获取的名额,用于下游调用失败后的补偿。 |
| | | */ |
| | | public void releaseFixedWindow(String key) { |
| | | if (StringUtil.isEmpty(key)) { |
| | | return; |
| | | } |
| | | String script = "local current = tonumber(redis.call('get', KEYS[1]) or '0'); " |
| | | + "if current <= 1 then redis.call('del', KEYS[1]); " |
| | | + "else redis.call('decr', KEYS[1]); end; return 1"; |
| | | try (Jedis resource = jedisPool.getResource()) { |
| | | resource.eval(script, Collections.singletonList(key), Collections.<String>emptyList()); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 批量获取 |
| | | * |
| | | * @param kes |