限流
1、预备知识
Pipeline(管道)本质上是由客户端提供的一种操作。Pipeline 通过调整指令列表的读写顺序,可以大幅度的节省 IO 时间,提高效率。
2、简单限流
public class RateLimiter {
private Jedis jedis;
public RateLimiter(Jedis jedis) {
this.jedis = jedis;
}
/**
* 限流方法
* @param user 操作的用户,相当于限流的对象
* @param action 具体的操作
* @param period 时间窗,限流的周期 (单位:ms)
* @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 pipeline = jedis.pipelined();
pipeline.multi();
// 4、将当前的操作先保存下来
pipeline.zadd(key, nowTime, String.valueOf(nowTime));
// 5、移除时间窗之外的数据
pipeline.zremrangeByScore(key, 0, nowTime - period * 1000);
// 6、统计剩下的 key
Response<Long> response = pipeline.zcard(key);
// 7、将当前的 key 设置一个过期时间, 过期时间就是时间窗
pipeline.expire(key, period + 1);
// 关闭通道
pipeline.exec();
pipeline.close();
// 8、比较时间窗的返回数
return response.get() <= maxCount;
}
public static void main(String[] args) {
Redis redis = new Redis();
redis.execute(j -> {
RateLimiter rateLimiter = new RateLimiter(j);
for (int i = 0; i < 20; i++) {
// 5 s之内只有前 3 次操作有效
System.out.println(rateLimiter.isAllowed("lucky", "publish", 5, 3));
}
});
}
}
3、深入限流操作
Redis4.0 开始提供了一个 Redis-Cell 模块,这个模块使用漏斗算法,提供了一个非常好用的限流指令。 漏斗算法就像名字一样,是一个漏斗,请求从漏斗的大口进,然后从小口出进入到系统中,这样,无论 是多大的访问量,最终进入到系统中的请求,都是固定的。
使用漏斗算法,需要我们首先安装 Redis-Cell 模块:
http://https//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
mkdir redis-cell
mv libredis_cell.d ./redis-cell
mv libredis_cell.so ./redis-cell
接下来修改 redis.conf 文件,加载额外的模块:
loadmodule /home/redis/redis-6.0.8/redis-cell/libredis_cell.so
![](https://img.haomeiwen.com/i13683989/4f530931d36788f3.png)
然后,启动 Redis:
redis-server redis.conf
redis 启动成功后,如果存在 CL.THROTTLE 命令,说明 redis-cell 已经安装成功了。
CL.THROTTLE 命令一共有五个参数
-
第一个参数是 key
-
第二个参数是漏斗的容量
-
时间窗内可以操作的次数
-
时间窗
-
每次漏出数量
执行完成后,返回值也有五个:
-
第一个 0 表示允许,1表示拒绝
-
第二个参数是漏斗的容量
-
第三个参数是漏斗的剩余空间
-
如果拒绝了,多长时间后,可以再试 (-1 没有拒绝)
-
多长时间后,漏斗会完全空出来
127.0.0.1:6379> CL.THROTTLE lucky 10 10 60 1
1) (integer) 0
2) (integer) 11
3) (integer) 10
4) (integer) -1
5) (integer) 6
127.0.0.1:6379>
Java操作:使用 Lettuce ,如下。
4、Lettuce扩展
Lettuce 依赖:
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
首先定义一个命令接口:
public interface RedisCommandInterface extends Commands {
/**
* 定义接口
* @param key key
* @param init 漏斗的容量
* @param count 时间窗内可以操作的次数
* @param period 时间窗
* @param quota 每次漏出数量
* @return
*/
@Command("CL.THROTTLE ?0 ?1 ?2 ?3 ?4")
List<Object> throttle(String key, Long init, Long count, Long period, Long quota);
}
定义完成后,接下来,直接调用即可:
public class ThrottleTest {
public static void main(String[] args) {
RedisClient redisClient = RedisClient.create("redis://lucky@172.81.247.170");
StatefulRedisConnection<String, String> connect = redisClient.connect();
RedisCommandFactory factory = new RedisCommandFactory(connect);
RedisCommandInterface commands = factory.getCommands(RedisCommandInterface.class);
List<Object> list = commands.throttle("lucky", 10L, 10L, 60L, 1L);
System.out.println(list);
}
}
网友评论