美文网首页
redis限流

redis限流

作者: 缘木与鱼 | 来源:发表于2021-05-08 12:15 被阅读0次

限流

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
module模块

然后,启动 Redis:

redis-server redis.conf

redis 启动成功后,如果存在 CL.THROTTLE 命令,说明 redis-cell 已经安装成功了。

CL.THROTTLE 命令一共有五个参数

  1. 第一个参数是 key

  2. 第二个参数是漏斗的容量

  3. 时间窗内可以操作的次数

  4. 时间窗

  5. 每次漏出数量

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

  1. 第一个 0 表示允许,1表示拒绝

  2. 第二个参数是漏斗的容量

  3. 第三个参数是漏斗的剩余空间

  4. 如果拒绝了,多长时间后,可以再试 (-1 没有拒绝)

  5. 多长时间后,漏斗会完全空出来

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);
    }
}

相关文章

网友评论

      本文标题:redis限流

      本文链接:https://www.haomeiwen.com/subject/ohqvrltx.html