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命令一共有五个参数:

  1. 第一个参数是key(要限流的操作)
  2. 第二个参数是漏斗的容量
  3. 第三个参数:时间戳内可以操作的次数
  4. 第四个参数:时间戳
  5. 第五个参数:每次漏出的数量(可选)
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

执行完成后,返回值也有五个:

  1. 第一个为0表示运行,1表示拒绝
  2. 第二个参数表示漏斗的容量
  3. 第三个参数是漏斗的剩余空间
  4. 如果拒绝了,多长时间后,可以再试
  5. 多长时间后,漏斗会完全空出来

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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值