学习目标:
1、了解限流算法
2、了解Redis-Cell的限流的使用
学习过程:
一、redis-cell命令
有很多业务场景需要做限流,比如有些论坛一分钟只能发表10份文章,如果每一次都要获得当前时间,再去数据库查询,效率是非常慢的,Redis提供了一个外部模块redis-cell,可以非常简单的实现限流。和bloom filter布隆过滤器一样需要额外安装。先登陆官网网址
https://github.com/brandur/redis-cell
因为redis-cell是使用rust语言编写,所以这里我们就不下载源码包了,直接下载编译后的版本,找到对应的操作系统的版本即可,里面有一个so文件,和bloom filter布隆过滤器再redis.conf中添加该模块,
vim /etc/redis.conf
loadmodule /usr/local/redis/module/rediscell/libredis_cell.so
保存退出就可以了,然后重启redis服务即可。
该模块只有一个命令,查看官方的说明:
CL.THROTTLE user123 15 30 60 1
▲ ▲ ▲ ▲ ▲
| | | | └───── apply 1 token (default if omitted)
| | └──┴─────── 30 tokens / 60 seconds
| └───────────── 15 max_burst
└─────────────────── key "user123"
user123 就是key
15 是max_burst,就是初始时,最大的容量,就是令牌桶初始时的数量,但是初始化数量是该值加一。
400: 与下一个参数一起,表示在指定时间窗口内允许访问的次数
30 和 60 就是速率,表示每60秒可以有30个令牌数量
1:最后一个表示本次要申请的令牌数,不写则默认为 1。
我们先执行一下命令
127.0.0.1:6379> CL.THROTTLE user123 15 30 60 1
1) (integer) 0 # 0 表示允许,1表示拒绝
2) (integer) 16 # 漏斗总容量 就是 15+1得到的。
3) (integer) 15 # 漏斗剩余空间,取得了一个所以剩下15
4) (integer) -1 # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
5) (integer) 2 # 表示多久后令牌桶中的令牌会存满(单位秒)
我们可以继续执行
127.0.0.1:6379> CL.THROTTLE user123 15 30 60 5
1) (integer) 0
2) (integer) 16
3) (integer) 11
4) (integer) -1
5) (integer) 10
127.0.0.1:6379> CL.THROTTLE user123 15 30 60 5
1) (integer) 0
2) (integer) 16
3) (integer) 7
4) (integer) -1
5) (integer) 17
127.0.0.1:6379> CL.THROTTLE user123 15 30 60 5
1) (integer) 0
2) (integer) 16
3) (integer) 3
4) (integer) -1
5) (integer) 25
127.0.0.1:6379> CL.THROTTLE user123 15 30 60 5
1) (integer) 1
2) (integer) 16
3) (integer) 3
4) (integer) 2
5) (integer) 24
因为每60秒可以有30次,所以过两秒又可以了。发现后面就返回1了,等一下再次执行又可以了。
127.0.0.1:6379> CL.THROTTLE user123 15 30 60 2
1) (integer) 0
2) (integer) 16
3) (integer) 14
4) (integer) -1
5) (integer) 4
一、redis-cell命令的java实现
因为是扩展命令,所以要先继承ProtocolCommand扩展命令,代码如下:
public enum CellCommand implements ProtocolCommand {
CLTHROTTLE("CL.THROTTLE");
private final byte[] raw;
CellCommand(String alt) {
raw = SafeEncoder.encode(alt);
}
public byte[] getRaw() {
return raw;
}
}
运行代码
@Test
public void testCell() {
Jedis jedis = new Jedis("192.168.137.101", 6379);
jedis.auth("123456");
Connection client = jedis.getClient();
client.sendCommand(CellCommand.CLTHROTTLE, "user123", "15", "30", "60", "2");
List<Long> replay = client.getIntegerMultiBulkReply();
for(Long s:replay) {
System.out.println(s);
}
client.close();
}
输出结果:
0
16
14
-1
4