本文是限流算法系列的终篇,将带你深入了解分布式环境下的限流实现。从Redis到集群,从原理到实战,全方位掌握分布式限流技术。
博主匠心之作,强推专栏:
- JAVA集合专栏 【夜话集】
- JVM知识专栏
- 数据库sql理论与实战【博主踩坑之道】
- 小游戏开发【博主强推 匠心之作 拿来即用无门槛】
说到分布式限流,最常见的需求是:限制某个接口每秒只能处理N个请求。这种场景下,固定窗口限流就是一个不错的选择。
为什么选择Redis+Lua这个组合呢?主要考虑两点:
让我们先看看具体实现:
-- 固定窗口限流脚本
local key = KEYS[1] -- 限流KEY
local limit = tonumber(ARGV[1]) -- 限流大小
local current = tonumber(redis.call('get', key) or "0")
-- 检查是否超过限制
if current + 1 > limit then
return 0
else
-- 计数器+1,并设置1秒过期
redis.call("INCRBY", key, 1)
redis.call("EXPIRE", key, 1)
return 1
end
这个脚本的逻辑很简单:用Redis的key记录1秒内的请求数,超过限制就返回失败。通过EXPIRE命令自动清理过期计数器,实现滑动窗口的效果。
接下来看看Java端如何调用这个脚本:
/**
* Redis限流器
* 使用Redis+Lua实现分布式限流
*/
public class RedisRateLimiter {
private final StringRedisTemplate redisTemplate;
/** 限流Lua脚本,保证原子性 */
private final String luaScript;
/**
* 构造函数
* @param redisTemplate Redis操作模板
*/
public RedisRateLimiter(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
// 加载Lua脚本
this.luaScript = loadLimitScript();
}
/**
* 尝试通过限流检查
* @param key 限流标识,如:order:create
* @param limit 限流阈值,如:每秒1000次
* @return true:通过限流;false:被限流
*/
public boolean isAllowed(String key, int limit) {
try {
// 封装Lua脚本参数
List<String> keys = Collections.singletonList(key);
// 执行Lua脚本
// 返回1表示通过限流,0表示被限流
return redisTemplate.execute(
new DefaultRedisScript<>(luaScript, Long.class),
keys,
String.