【高并发】亿级流量场景下如何实现分布式限流?

291 阅读2分钟

分布式限流的关键就是需要将限流服务做成全局的,统一的。可以采用Redis+Lua技术实现,通过这种技术可以实现高并发和高性能的限流。

Lua是一种轻量小巧的脚本编程语言,用标准的C语言编写的开源脚本,其设计的目的是为了嵌入到应用程序中,为应用程序提供灵活的扩展和定制功能。

Redis+Lua脚本实现分布式限流思路

我们可以使用Redia+Lua脚本的方式来对我们的分布式系统进行统一的全局限流,Redis+Lua实现的Lua脚本:

-- 下标从 1 开始local key = KEYS[1]

local now = tonumber(ARGV[1])

local ttl = tonumber(ARGV[2])

local expired = tonumber(ARGV[3])-- 最大访问量

local max = tonumber(ARGV[4])-- 清除过期的数据-- 移除指定分数区间内的所有元素,expired 即已经过期的 score-- 根据当前时间毫秒数 - 超时毫秒数,得到过期时间 expired

redis.call('zremrangebyscore', key, 0, expired)-- 获取 zset 中的当前元素个数

local current = tonumber(redis.call('zcard', key))

local next = current + 1if next > max then -- 达到限流大小 返回 0 

 return 0;else -- 往 zset 中添加一个值、得分均为当前时间戳的元素,[value,score] 

 redis.call("zadd", key, now, now) -- 每次访问均重新设置 zset 的过期时间,单位毫秒 redis.call("pexpire", key, ttl) 

 return next

end

我们可以按照如下的思路来理解上述Lua脚本代码。

(1)在Lua脚本中,有两个全局变量,用来接收Redis应用端传递的键和其他参数,分别为:KEYS、ARGV;

(2)在应用端传递KEYS时是一个数组列表,在Lua脚本中通过索引下标方式获取数组内的值。

(3)在应用端传递ARGV时参数比较灵活,可以是一个或多个独立的参数,但对应到Lua脚本中统一用ARGV这个数组接收,获取方式也是通过数组下标获取。

(4)以上操作是在一个Lua脚本中,又因为我当前使用的是Redis 5.0版本(Redis 6.0支持多线程),执行的请求是单线程的,因此,Redis+Lua的处理方式是线程安全的,并且具有原子性。

这里,需要注意一个知识点,那就是原子性操作:如果一个操作时不可分割的,是多线程安全的,我们就称为原子性操作。

接下来,我们可以使用如下Java代码来判断是否需要限流。