先安装redis,可以参考我另一篇博文
https://blog.csdn.net/weixin_43944305/article/details/120661885
安装redis-cell
https://github.com/brandur/redis-cell/releases/
我用的ubuntu系统
下载后解压,然后将libredis_cell.so
文件的路径配置到redis
的配置文件redis.conf
中,然后启动并登陆redis
,用MODULE LIST
命令查看module
是否加载成功
loadmodule /home/ubuntu/download/redisCell/libredis_cell.so
命令行测试令牌桶功能
cl.throttle liziba 10 5 60 1
CL.THROTTLE liziba 10 5 60 1
▲ ▲ ▲ ▲ ▲
| | | | └───── apply 1 token (default if omitted) (本次申请一个token)
| | └──┴─────── 5 tokens / 60 seconds (60秒添加5个token到令牌桶中)
| └───────────── 10 max_burst (最大的突发请求,不是令牌桶的最大容量)
└─────────────────── key "liziba" (限流key)
命令的响应结果
127.0.0.1:6379> cl.throttle liziba 10 5 60 1
1) (integer) 0 # 当前请求是否被允许,0表示允许,1表示不允许
2) (integer) 11 # 令牌桶的最大容量,令牌桶中令牌数的最大值
3) (integer) 10 # 令牌桶中当前的令牌数
4) (integer) -1 # 如果被拒绝,需要多长时间后在重试,如果当前被允许则为-1
5) (integer) 12 # 多长时间后令牌桶中的令牌会满
java使用RedisTemplate测试令牌桶
引入连接redis
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
这里要注意如果使用自带的RedisTemplate
的话执行脚本后redis
服务端会莫名挂掉,这是因为自带的RedisTemplate
使用了JDK
的序列化策略,导致redis
服务端收不到命令,所以要修改并使用string
序列化策略
@Bean
public RedisTemplate<String,Object> myRedisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
ObjectMapper objectMapper = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
//兼容java8的时间api
objectMapper.registerModule(new JavaTimeModule());
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
GenericJackson2JsonRedisSerializer jacksonSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
// 值采用json序列化
template.setValueSerializer(jacksonSerializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jacksonSerializer);
template.afterPropertiesSet();
return template;
}
/**
* 请求是否被允许
* @param key 限流的key
* @param maxBurst 最大请求
* @param tokens 令牌数量 每过seconds时间就往桶里放入tokens的令牌数量
* @param seconds 时间 每过seconds时间就往桶里放入tokens的令牌数量
* @param apply 本次获取几个令牌
* @return
*/
public boolean isActionAllowed(String key, int maxBurst, int tokens, int seconds, int apply){
DefaultRedisScript<List> script = new DefaultRedisScript<>("return redis.call('cl.throttle',KEYS[1],ARGV[1],ARGV[2],ARGV[3],ARGV[4])", List.class);
List<Long> rst = myRedisTemplate.execute(script, Arrays.asList(key), maxBurst, tokens, seconds, apply);
//响应结果
/**
* 1) (integer) 0 # 当前请求是否被允许,0表示允许,1表示不允许
* 2) (integer) 11 # 令牌桶的最大容量,令牌桶中令牌数的最大值
* 3) (integer) 10 # 令牌桶中当前的令牌数
* 4) (integer) -1 # 如果被拒绝,需要多长时间后在重试,如果当前被允许则为-1
* 5) (integer) 12 # 多长时间后令牌桶中的令牌会满
*/
return rst.get(0) == 0;
}