平时项目中做分布式限流可能会使用一些框架,如Sentinel,Hystrix等开源框架.今天介绍一种自己开发的分布式限流方案.使用Redis+Lua的方式.
简介
平时研发中可能存在这种场景.调用外部API接口时由于外部接口存在限频,如阿里云短信发送短信接口每秒只允许调用100次,超过这个频次后请求被拒绝.对于这种场景不清楚Sentinel和Hystrix等是否可以使用.那么为了保证短信消息的不丢失,短信系统需要针对外部接口的限频做一些消息可靠性保证.本文已短信消息为原型进行介绍分布式限流的方案.
方案
Redis+Lua+MQ实现限流的同时保证消息不丢失与及时发送
1.首先使用Redis的list存储消息的消息到来时刻的时间戳
2.redis+lua保证分布式系统中操作的原子性
3.消息队列(RocketMQ)的延迟性保证消息不丢失与及时发送
首先定义几个变量:
限频数量阀值:100
限频时间阀值:1min
即1min只允许发送100条消息
下面我们看看一条消息是如何发送出去的.
- 首先一条消息来到了我们的限流器,看list当中是否存在100条数据,如果没有达到100条数据即可立即发送该消息.那么将当前消息的时间戳存储到list当中.
- 如果list当中已经存在100条数据了,那么需要进一步判断当前消息的时间戳与list头的时间戳的差值t是否大于1min,如果大于1min说明可以进行下一分钟的消息发送了,那么将list头中的时间戳出队.将当前消息时间戳入队
- 如果当前时间戳与list队头时间戳差值t不足1min,那么将当前消息发送到RocketMQ的延迟topic中,延迟时间为1min-t.延迟时间到达时即可发送.
Redis+lua
local function addToQueue(x,time)
local count=0
for i=1,x,1 do
redis.call('lpush',KEYS[1],time)
count=count+1
end
return count
end
local result=0
local timeBase = redis.call('lindex',KEYS[1], tonumber(ARGV[2])-tonumber(ARGV[1]))
if (timeBase == false) or (tonumber(ARGV[4]) - tonumber(timeBase)>tonumber(ARGV[3])) then
result=result+addToQueue(tonumber(ARGV[1]),tonumber(ARGV[4]))
end
if (timeBase~=false) then
redis.call('ltrim',KEYS[1],0,tonumber(ARGV[2])-1)
end
return result
如何在代码中使用lua脚本
public boolean acquirePromise(String redisKey,long count, long rateCount, long rateTime) {
validate(redisKey, count, rateCount, rateTime);
String luaScript = ScriptUtil.getScript("rateLimit.lua");
RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
Long currMillSecond = redisTemplate.execute((RedisCallback<Long>) redisConnection -> redisConnection.time());
Number result = redisTemplate.execute(redisScript, Collections.singletonList(redisKey), String.valueOf(count), String.valueOf(rateCount), String.valueOf(rateTime), String.valueOf(currMillSecond));
if(result != null && result.intValue() > 0) {
//放行
return true;
}
//限流
return false;
}
Tips:
1.如果使用RocketMQ的延迟Topic需要使用阿里云版本的RocketMQ,因为开源的RocketMQ不支持毫秒级别的延迟
2.redis 4.0 以后开始支持扩展模块,redis-cell是一个用rust语言编写的基于令牌桶算法的的限流模块,提供原子性的限流功能,并允许突发流量.
版权声明:本文为u014439693原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。