用Java实现Redis限流
1、概述
限流的含义是在单位时间内确保发往某个模块的请求数量小于某个数值,比如在实现秒杀功能时,需要确保在10秒内发往支付模块的请求数量小于500个。限流的作用是防止某个段时间段内的请求数过多,造成模块因高并发而不可用。
2、zset有序集合相关命令与限流
zset也叫有序集合,是Redis的一种数据类型,在其中每个值(value)都会有一个对应的score参数,以此来描述该值的权重分值。可以通过如下形式的命令向zset有序集合里添加元素:
zadd key score value
在限流相关的应用里,可以通过zadd命令往有序集合里存放数据时引入表示权重的score参数,并在其中存放时间戳。由于zset是有序集合,因此包含时间戳的score能被排序,这样就能用zremrangeByScore命令去除指定时间范围内的数据。zrenrabgeByScore命令的语法如下:
zremrangeByScore key min max
通过该命令能在有序集合里删除键为key、score值在min到max范围内的数据。通过这种删除动作,能排除限流时间范围外的数据,并能在此基础上通过zcard命令统计有序集合内元素的数量,以确保请求数量小于限流的上限值。zcard命令的语法格式如下:
zcard key
该命令会返回有序集合内指定key的元素数量。
3、zset有序结合与限流
有关redis启动的知识不做过多介绍了,这里我用docker创建的redis容器。
本例实现100秒内只能处理3个请求
LimitRequest.java
package com.baizhi.request;
import redis.clients.jedis.Jedis;
public class LimitRequest {
public static void main(String[] args) {
Jedis jedis=new Jedis("192.168.159.33",6379);
jedis.del("PayRequest");
//模拟发5个请求
int cnt=5;
for (int i = 0; i < cnt; i++) {
LimitUtil.canVisit(jedis,"PayRequest",100,3);
}
}
}
class LimitUtil{
//判断是否需要限流
public static void canVisit(Jedis jedis,String requestType,int limitTime,int limitNum){
long currentTime=System.currentTimeMillis();
//把请求放入zset
jedis.zadd(requestType,currentTime,Long.valueOf(currentTime).toString());
//去掉时间范围外(超时)的请求
jedis.zremrangeByScore(requestType,0,currentTime-limitTime*1000);
//统计时间范围内总数
Long count=jedis.zcard(requestType);
//设置所有请求的超时时间
jedis.expire(requestType,limitTime+1);
boolean flag=limitNum>=count;
if(flag){
System.out.println("Can visit");
}else{
System.out.println("Can not visit");
}
}
}
先通过zadd方法把表示操作类型的requestType作为键插入有序集合,插入时用表示当前时间的currentTime作为值,以保证值的唯一性,同时用currentTime作为有序集合里元素的score值。
随后通过zremrangeByScore命令去除0到距当前时间limitTime时间范围内的数据。比如限流的时间范围是100秒,那么通过zremrangeByScore命令就能在有序集合里去除score范围从0到距离当前时间100秒的数据,这样就能确保有序集合内只存有最近100秒内发来的元素。
其中,用zcard命令统计有序集合内键为requestType的个数,如果通过if语句发现当前个数还没有达到限流的上限,则允许该请求方法,否则不允许。这里通过最后的打印语句来模拟“是否允许请求访问”的动作。
同时请注意,需要使用expire语句设置有序集合里相关键的超时时间,这样就能确保在限流动作完成后这些键能自动删除,而不是一致驻留在内存中。
在main函数里,首先创建指向虚拟机6379端口的jedis对象,为了确保在多次运行时数据不相互干扰,在运行前通过del语句删除相关的键。
随后使用for循环里通过调用canVisit方法模拟发出5个请求。在调用该方法时,通过传入参数指定限流所用的键为"PayRequest",同时指定了100秒内只能处理3个请求。运行之后控制台的效果如下:
- 点赞
- 收藏
- 关注作者
评论(0)