redis限流的使用
1.简单限流
1.实现原理:通过zset实现!
2.pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>distribute-lock</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--json依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<!--jedis依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<!--bloom依赖-->
<dependency>
<groupId>com.redislabs</groupId>
<artifactId>jrebloom</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
</project>
3.JedisUtils
package com.yl;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisUtils {
private static JedisPool jedisPool = null;
public static Jedis getJedisObject() {
if (jedisPool == null) {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
//最大空闲数
config.setMaxIdle(400);
//最大连接数
config.setMaxTotal(2000);
//连接最大等待时间,-1代表没有限制
config.setMaxWaitMillis(300000);
/**
* 配置连接池的地址,端口号,超时时间,密码
*/
jedisPool = new JedisPool(config,"192.168.244.129",6379,30000,"root123");
}
try {
//通过连接池获取jedis对象
Jedis jedis = jedisPool.getResource();
jedis.auth("root123");
return jedis;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
4.实现限流
package com.yl;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
/**
* 限流
*/
public class CurrentLimiting {
private Jedis jedis;
public CurrentLimiting(Jedis jedis) {
this.jedis = jedis;
}
/**
*
* @param user 限流对象
* @param action 具体操作
* @param period 周期
* @param maxCount 限流次数
* @return 返回true代表可以操作,还没限流,返回false代表限流,不可以操作
*/
public boolean isPermit(String user,String action,int period,int maxCount) {
//用zset来实现
//生成一个key
String key = user + "->" + action;
long timeMillis = System.currentTimeMillis();
//建立管道
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
//将当前操作保存下来
pipeline.zadd(key,timeMillis,String.valueOf(timeMillis));
//移除时间窗之外的数据
pipeline.zremrangeByScore(key,0,timeMillis - period * 1000);
//统计剩下的key
Response<Long> zcard = pipeline.zcard(key);
//给key设置过期时间
pipeline.expire(key,period + 1);
//关闭管道
pipeline.exec();
pipeline.close();
return zcard.get() <= maxCount;
}
}
5.测试
package com.yl;
import redis.clients.jedis.Jedis;
public class LimitTest {
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedisObject();
CurrentLimiting limiting = new CurrentLimiting(jedis);
for (int i = 0; i < 20; i++) {
System.out.println(limiting.isPermit("yl", "editUser", 5, 3));
}
}
}
2.通过Redis-Cell实现限流
-
Redis-Cell模块也可以解决限流问题,其内部用到的算法为漏斗算法,请求从漏斗的大口进,从小口出,再进入到系统!
-
下载并且安装Redis-Cell模块
mkdir redis-cell
cd redis-cell/
wget https://github.com/brandur/redis-cell/releases/download/v0.2.5/redis-cell-v0.2.5-x86_64-unknown-linux-gnu.tar.gz
tar zxvf redis-cell-v0.2.5-x86_64-unknown-linux-gnu.tar.gz
- 在redis.conf中加载Redis-Cell模块
4.重启redis后,如果发现连接不上redis
可参考这篇文章解决:链接: https://blog.csdn.net/hxxx1314/article/details/115109098.
- 重启redis,连接到redis,发现CL.THROTTLE命令可用就代表redis-cell安装且配置成功!
5.CL.THROTTLE命令参数介绍
第一个是key
第二个是漏斗的容量
第三个是时间窗内可以操作的次数
第四个是时间窗
第五个是每次漏出数量
命令执行成功也返回五个值
第一个,返回0代表允许,返回1代表拒绝
第二个,漏斗的容量(返回的值-1才是容量值)
第三个,漏斗的剩余空间
第四个,如果拒绝了,多长时间后可以再试
第五个,多行时间后,漏斗会完全空出来
3.lettuce自定义命令实现限流
1.pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>lettuce</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
</dependencies>
</project>
- 自定义接口
package com.yl.config;
import io.lettuce.core.dynamic.Commands;
import io.lettuce.core.dynamic.annotation.Command;
import java.util.List;
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);
}
- 测试
package com.yl.config;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.dynamic.RedisCommandFactory;
import java.util.List;
public class ThrottleTest {
public static void main(String[] args) {
//创建client对象
RedisClient redisClient = RedisClient.create("redis://root123@192.168.244.129");
//获取连接对象
StatefulRedisConnection<String, String> connect = redisClient.connect();
RedisCommandFactory redisCommandFactory = new RedisCommandFactory(connect);
RedisCommandInterface commands = redisCommandFactory.getCommands(RedisCommandInterface.class);
List<Object> list = commands.throttle("yl-update", 10L, 10L, 60L, 1L);
System.out.println(list);
}
}