Redis之限流
1 介绍
Pipeline(管道)本质上是由客户端提供的一种操作。Pipline通过调整指令列表的读写顺序,可以大幅度节省IO时间,提高效率。
2 简单限流
public class RateLimiter {
private Jedis jedis;
public RateLimiter(Jedis jedis) {
this.jedis = jedis;
}
/**
*
* @param user 操作的用户,相当于是限流的对象
* @param action 具体的操作
* @param period 时间戳,限流的周期
* @param maxCount 限流的次数
* @return
*/
public boolean isAllowed(String user,String action,int period,int maxCount){
//1.数据用zset保存,首先生成一个key
String key=user+"-"+action;
//2.获取当前时间戳
long nowTime = System.currentTimeMillis();
//3.建立管道
Pipeline pipelined = jedis.pipelined();
pipelined.multi();//开启管道
//4.将当前操作存储下来,后面是key,value
pipelined.zadd(key,nowTime,String.valueOf(nowTime));
//5.移除时间戳之外的数据
pipelined.zremrangeByScore(key,0,nowTime-period*1000);//移除30秒之前的数据
//6.统计剩下的key
Response<Long> response = pipelined.zcard(key);
//7.将当前key设置一个过期时间,过期时间就是时间戳(不设置的话,在5也会移除,这里加保险一点)
pipelined.expire(key,period+1);
//关闭管道
pipelined.exec();//执行
pipelined.close();//关闭
//8.比较时间戳内的操作数
return response.get()<=maxCount;
}
public static void main(String[] args) {
Redis redis = new Redis();
redis.exectu(jedis1 -> {
RateLimiter rateLimiter = new RateLimiter(jedis1);
for (int i = 0; i <20 ; i++) {
//1秒之内只能3次操作,
System.out.println(rateLimiter.isAllowed("java","publish",1,3));
}
});
}
}
3 深入限流操作
Redis4.0开始提供了一个Redis-Cell模块,这个模块使用漏斗算法,提供了一个非常好用的限流指令。
漏斗算法就像名字一样,是一个漏斗,请求从漏斗的大口进,然后从小口出,进入到系统中,这样,无论是多大的访问量,最终进入到系统中的请求,都是固定的。
使用漏斗算法,需要我们首先安装Redis-Cell模块
官网链接:github.com/brandur/redis-cell
安装步骤:
wget https://github.com/brandur/redis-cell/releases/download/v0.2.4/redis-cell-v0.2.4-x86_64-unknown-linux-gnu.tar.gz
tar -zxvf redis-cell-v0.2.4-x86_64-unknown-linux-gnu.tar.gz
//解压出两个文件
libredis_cell.d
libredis_cell.so
mkdir redis-cell
//创建目录
[root@VM-192-15-centos redis-5.0.7]# mv libredis_cell.d ./redis-cell
[root@VM-192-15-centos redis-5.0.7]# mv libredis_cell.so ./redis-cell
//移动到redis-cell下
接下来修改redis.conf文件,加载额外的模块
进入配置文件
vi redis.conf
添加
loadmodule /root/redis-5.0.7/redis-cell/libredis_cell.so
然后启动redis
redis-server redis.conf
redis启动成功后,如果存在CL.THROTTLE命令,说明redis-cell已经安装成功了
失败的话,需要下载glibc文件
// 下载 glibc 压缩包
wget http://ftp.gnu.org/gnu/glibc/glibc-2.18.tar.gz
// 解压 glibc 压缩包
tar -zxvf glibc-2.18.tar.gz
// 进入解压后的目录
cd glibc-2.18
// 创建编译目录
mkdir build
// 进入到创建好的目录
cd build/
// 编译、安装
../configure --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
// 这一步比较慢
make -j 8
make install
//再次测试即可启动成功
redis-server redis.conf
ps -ef | grep redis
// 即可看到redis启动成功
看到下面的redis-server即为成功
[root@VM-192-15-centos redis-5.0.7]# ps -ef | grep redis
root 21480 1 0 14:57 ? 00:00:00 redis-server *:6379
root 21518 13652 0 14:57 pts/0 00:00:00 grep --color=auto redis
CL.THROTTLE命令一共有五个参数:
- 第一个参数是key(要限流的操作)
- 第二个参数是漏斗的容量
- 第三个参数:时间戳内可以操作的次数
- 第四个参数:时间戳
- 第五个参数:每次漏出的数量(可选)
127.0.0.1:6379> CL.THROTTLE java-pub 10 10 60
1) (integer) 0
2) (integer) 11
3) (integer) 10
4) (integer) -1
5) (integer) 6
执行完成后,返回值也有五个:
- 第一个为0表示运行,1表示拒绝
- 第二个参数表示漏斗的容量
- 第三个参数是漏斗的剩余空间
- 如果拒绝了,多长时间后,可以再试
- 多长时间后,漏斗会完全空出来
4 Lettuce应用
Java服务端操作:
首先定义一个命令接口:
添加lettuce依赖
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
public interface RedisCommandInterface extends Commands {
/**
* 1.第一个参数是key(要限流的操作)
* 2.第二个参数是漏斗的容量
* 3.第三个参数:时间戳内可以操作的次数
* 4.第四个参数:时间戳
* 5.第五个参数:每次漏出的数量(可选)
* @return
*/
@Command("CL.THROTTLE ?0 ?1 ?2 ?3 ?4")
List<Object> throttle(String key,Long init,Long count,Long period,Long quota);
}
创建接口,相当于扩展了一个命令
定义完成后,接下来,直接调用即可:(都可用lettuce来自定义命令)
public class ThrottleTest {
public static void main(String[] args) {
RedisClient redisClient = RedisClient.create("redis://password@127.0.0.1");
StatefulRedisConnection<String, String> connect = redisClient.connect();
RedisCommandFactory redisCommandFactory = new RedisCommandFactory(connect);
RedisCommandInterface commands = redisCommandFactory.getCommands(RedisCommandInterface.class);
List<Object> throttle = commands.throttle("java-publish", 10L, 10L, 60L, 1L);
System.out.println(throttle);
}
}