Redis+Lua实现分布式限流

2018-06-09 10:38:00
1171 次阅读
0 个评论

限流接口


public interface EasyRateLimiter {
 
    void acquire(int permits);
}


调用示例


@Resource(name = "redisEasyRateLimiterImpl")
private EasyRateLimiter easyRateLimiter;
 
@Test
public void test() throws InterruptedException {
    LOGGER.info("限流前");
    easyRateLimiter.acquire(1);
    LOGGER.info("限流后");
}


限流实现

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
 
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
 

public class RedisEasyRateLimiterImpl implements EasyRateLimiter {
 
    private static final Logger LOGGER = Logger.getLogger(RedisEasyRateLimiterImpl.class.getName());
 
    private String redisKey;
 
    private Integer qps;// 限流器速率,自行注入
 
    private RedisTemplate<String, Long> redisTemplate;// spring redis template,需要注意,由于需要与lua脚本交互,Key、Value需要使用String序列化
 
    private RedisScript<Long> redisScript;// spring redis script
 
    public void acquire(int permits) {
        Long currentTime = System.currentTimeMillis();
        //@TODO 调用TIME命令获取统一时间
        //redisTemplate.getConnectionFactory().getConnection().time();//如此获取会造成连接泄漏
        LOGGER.info("current time: " + currentTime);
        Long holdTime = redisTemplate.execute(redisScript, Collections.singletonList(redisKey),
                String.valueOf(currentTime), String.valueOf(qps), String.valueOf(permits), UUID.randomUUID().toString().replace("-", ""));
        LOGGER.info("holdTime ms: " + holdTime);
        if (holdTime > 0) {
            try {
                TimeUnit.MILLISECONDS.sleep(holdTime);// 当前线程睡眠等待
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
 
    // setter and getter...
}

lua脚本

local nextPermissionTime = redis.call('GET', KEYS[1])
local currentTime = tonumber(ARGV[1])
local qps = tonumber(ARGV[2])
local permits = tonumber(ARGV[3])
local blockingTime = 0
 
if nextPermissionTime then
    nextPermissionTime = tonumber(nextPermissionTime)
    if nextPermissionTime > currentTime then
        blockingTime = nextPermissionTime - currentTime
    else
        nextPermissionTime = currentTime
    end
else
    nextPermissionTime = currentTime
end
 
nextPermissionTime = nextPermissionTime + 1 / qps * permits * 1000
redis.call('SET', KEYS[1], nextPermissionTime)
return blockingTime








收藏00

登录后回答。没有帐号?注册一个。