private void waitForToken(String tenantId) throws InterruptedException {
String key = RATE_LIMIT_KEY_PREFIX + tenantId;
long maxTokens = 5; // 最大令牌数
long refillInterval = 1000; // 令牌补充间隔(ms)
while (true) {
// 使用 Lua 脚本保证原子性
String luaScript =
"local current_tokens = redis.call('GET', KEYS[1])\n" +
"local last_refill_time = redis.call('GET', KEYS[2])\n" +
"local now = tonumber(ARGV[1])\n" +
"local max_tokens = tonumber(ARGV[2])\n" +
"local refill_interval = tonumber(ARGV[3])\n" +
"\n" +
"if current_tokens == false then\n" +
" redis.call('SET', KEYS[1], max_tokens - 1)\n" +
" redis.call('SET', KEYS[2], now)\n" +
" return 1\n" +
"end\n" +
"\n" +
"current_tokens = tonumber(current_tokens)\n" +
"last_refill_time = tonumber(last_refill_time)\n" +
"\n" +
"local tokens_to_add = math.floor((now - last_refill_time) / refill_interval)\n" +
"if tokens_to_add > 0 then\n" +
" current_tokens = math.min(max_tokens, current_tokens + tokens_to_add)\n" +
" last_refill_time = now\n" +
"end\n" +
"\n" +
"if current_tokens > 0 then\n" +
" redis.call('SET', KEYS[1], current_tokens - 1)\n" +
" redis.call('SET', KEYS[2], last_refill_time)\n" +
" return 1\n" +
"else\n" +
" return 0\n" +
"end";
List<String> keys = Arrays.asList(key, key + ":last_refill");
List<String> args = Arrays.asList(
String.valueOf(System.currentTimeMillis()),
String.valueOf(maxTokens),
String.valueOf(refillInterval)
);
Long result = stringRedisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
keys,
args.toArray(new String[0])
);
if (result != null && result == 1) {
// 获取到令牌
break;
} else {
// 没有获取到令牌,等待一段时间后重试
Thread.sleep(100);
}
}
}
/**
* 归还令牌(可选,根据业务需求决定是否需要)
* @param tenantId 租户ID
*/
private void returnToken(String tenantId) {
try {
String key = RATE_LIMIT_KEY_PREFIX + tenantId;
String luaScript =
"local current_tokens = redis.call('GET', KEYS[1])\n" +
"local max_tokens = tonumber(ARGV[1])\n" +
"\n" +
"if current_tokens ~= false then\n" +
" current_tokens = tonumber(current_tokens)\n" +
" if current_tokens < max_tokens then\n" +
" redis.call('SET', KEYS[1], current_tokens + 1)\n" +
" end\n" +
"end";
stringRedisTemplate.execute(
new DefaultRedisScript<>(luaScript),
Arrays.asList(key),
String.valueOf(5)
);
} catch (Exception e) {
log.warn("归还令牌失败,tenantId={}", tenantId, e);
}
}
}
基于redis+lua脚本实现限流
最新推荐文章于 2025-12-03 16:51:03 发布
1117

被折叠的 条评论
为什么被折叠?



