6.1 概览
4.2.2节实现了访问频率限制功能,可以限制一个IP地址在1分钟内最多只能访问100次:
$isKeyExists=EXISTS rate.limiting:$IP
if $isKeyExists is 1
$times=INCR rate.limiting:$IP
if $times > 100
print访问频率超过了限制,请稍后再试。
exit
else
MULTI
INCR rate.limiting:$IP
EXPIRE $keyName, 60
EXEC
当时提到上面的代码会出现竞态条件,解决方法是用WATCH命令检测rate.limiting:$IP键的变动,但是这样做比较麻烦,而且还需要判断事务是否因为键被改动而没有执行。除此之外这段代码在不使用管道的情况下最多要向Redis请求5条命令,在网络传输上会浪费很多时间。
我们这时最希望的就是Redis直接提供一个“RATELIM ITING”命令用来实现访问频率限制功能,这个命令只需要我们提供键名、时间限制和在时间限制内最多访问的次数三个参数就可以直接返回访问频率是否超限。就像这样。
if RATELIMITING rate.limiting:$IP, 60, 100
print访问频率超过了限制,请稍后再试。
else
# 没有超限,显示博客内容
这种方式不仅代码简单、没有竞态条件(Redis的命令都是原子的),而且减少了通过网络发送和接收命令的传输开销。然而可惜的是Redis并没有提供这个命令,不过我们可以使用Redis脚本功能自己定义新的命令。
6.1.1 脚本介绍
Redis在2.6版推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。在Lua脚本中可以调用大部分的Redis命令,也就是说可以将6.1节中的第一段代码改写成Lua脚本后发送给Redis执行。使用脚本的好处如下。
(1)减少网络开销:6.1节中的第一段代码最多需要向Redis发送5次请求,而使用脚本功能完成同样的操作只需要发送一个请求即可,减少了网络往返时延。
(2)原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说在编写脚本的过程中无需担心会出现竞态条件,也就无需使用事务。事务可以完成的所有功能都可以用脚本来实现。
(3)复用:客户端发送的脚本会永久存储在Redis中,这就意味着其他客户端(可以是其他语言开发的项目)可以复用这一脚本而不需要使用代码完成同样的逻辑。
6.1.2 实例:访问频率限制
因为无需考虑事务,使用Redis脚本实现访问频率限制非常简单。Lua代码如下:
local times=redis.call('incr', KEYS[1])
if times==1 then
-- KEYS[1]键刚创建,所以为其设置生存时间
redis.call('expire', KEYS[1], ARGV[1])
end
if times>tonumber(ARGV[2]) then
return 0
end
return 1
这段代码实现的功能与我们之前所做的类似,不过简洁了很多,即使不了解Lua语言也能猜出大概的意思。如果有的地方看不懂也没关系,6.2节会专门介绍Lua的语法和调用Redis命令的方法。
那么,如何测试这个脚本呢?首先把这段代码存为ratelimiting.lua,然后在命名行中输入:
$redis-cli --eval /path/to/ratelimiting.lua rate.limiting:127.0.0.1 , 10 3
其中--eval 参数是告诉redis-cli读取并运行后面的Lua脚本,/path/to/ratelimiting.lua是ratelimiting.lua文件的位置,后面跟着的是传给Lua脚本的参数。其中“,”前的rate.limiting:127.0.0.1是要操作的键,可以在脚本中使用KEYS[1]获取,“,”后面的10和3是参数,在脚本中能够使用ARGV[1]和ARGV[2]获得。结合脚本的内容可知这行命令的作用就是将访问频率限制为每10秒最多3次,所以在终端中不断地运行此命令会发现当访问频率在10秒内小于或等于3次时返回1,否则返回0。
注意 上面的命令中“,”两边的空格不能省略,否则会出错。
对于KEYS和ARGV两个变量会在6.3节中详细介绍,在下一节中我们会专门介绍Lua的语法。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论