Redis-Cell实现分布式限流

环境要求

基于redis 4.0 的单点或者集群

GLIBC_2.18

redis-cell使用说明

命令示例:CL.THROTTLE key_test 100 400 60 3

CL.THROTTLE: redis命令

key_test : redis key

100: 官方叫max_burst,初始值,不能为0

200: 在指定时间窗口内允许访问的次数

60: 指定的时间窗口,单位:秒

3: 表示本次要申请的令牌数,不写则默认为 1

响应如下:

127.0.0.1:6379> CL.THROTTLE test 100 400 60 3
1) (integer) 0
2) (integer) 101
3) (integer) 98
4) (integer) -1
5) (integer) 0

解释:

1): 是否成功,0:成功,1:拒绝

2): 令牌桶的容量,大小为初始值+1

3): 当前令牌桶中可用的令牌

4): 若请求被拒绝,这个值表示多久后才令牌桶中会重新添加令牌,单位:秒,可以作为重试时间

5): 表示多久后令牌桶中的令牌会存满

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

解压得到libredis_cell.so
# 解压
tar -zxvf redis-cell/releases/download/v0.2.5/redis-cell-v0.2.5-x86_64-unknown-linux-gnu.tar.gz

# 修改redis.conf文件配置,新增配置
loadmodule ${libredis_cell.so路径}

比如:

在这里插入图片描述

如果是redis集群,则所有node均需要如上安装。

重启redis后可执行 CL.THROTTLE key_test 100 400 60 3,如果正确输出则说明redis-cell安装成功。

备注:可能在配置redis-cell后redis重启失败,查看redis日志如下图示:

在这里插入图片描述

可知需要安装GLIBC 2.18,详情参考:

https://blog.csdn.net/qq_39295044/article/details/86685789

JAVA代码实现

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;

public class RedisCellDistributedRateLimiter {
     /** jedis命令客户端*/
    protected JedisCommands jedis;

    /** 自定义限流key*/
    protected String key;

    /** 时间窗口 单位秒*/
    protected int period;

    /**最大令牌容量*/
    protected int maxCapacity;
    
    public RedisCellDistributedRateLimiter(JedisCommands jedis, String key, int period, int maxCapacity) {
        this.jedis = jedis;
        this.key = key;
        this.period = period;
        this.maxCapacity = maxCapacity;
    }

	/**
     * 尝试申请资源
     * @param quote 目标资源数
     * @return 是否申请成功
     */
    public boolean tryAcquire(int quote) throws IOException {
        List<String> keys = new ArrayList<>();
        keys.add(key);
        List<String> argvs = new ArrayList<>();
        argvs.add(String.valueOf(maxCapacity));
        argvs.add(String.valueOf(maxCapacity));
        argvs.add(String.valueOf(period));
        argvs.add(String.valueOf(quote));
        Object result = null;
        if (jedis instanceof Jedis) {
            result = ((Jedis) this.jedis).eval(LUA_SCRIPT, keys, argvs);
        } else if (jedis instanceof JedisCluster) {
            result = ((JedisCluster) this.jedis).eval(LUA_SCRIPT, keys, argvs);
        } else {
            throw new RuntimeException("redis instance is error") ;
        }
        return ((List<Long>)result).get(0) == 0;
    }

    public static final String LUA_SCRIPT = "local key = KEYS[1]\n"+
            "local init_burst = tonumber(ARGV[1])\n"+
            "local max_burst = tonumber(ARGV[2])\n"+
            "local period = tonumber(ARGV[3])\n"+
            "local quota = ARGV[4]\n"+
            "return redis.call('CL.THROTTLE',key,init_burst,max_burst,period,quota)";
}

测试程序:

public static void main(String[] args) throws Exception{
        //构造Jedis集群
        Set<HostAndPort> nodeList  = new HashSet<>();
        nodeList.add( new HostAndPort("192.168.181.1", 6237));
        JedisCluster jedisCluster = 
            new JedisCluster(nodeList,10000,10000,10,"****", new JedisPoolConfig());
        String key = "redis-cell-test";
        int period = 5;
        int maxCapacity = 1;
        //构造限流器
        RedisCellDistributedRateLimiter limiter =
                new RedisCellDistributedRateLimiter(jedisCluster, key, period, maxCapacity);
        for (;;){
            CommonMethod.sleep(1000);
            if (limiter.tryAcquire(1)){
                System.out.println(("通过限流"));
            }else {
                System.out.println(("无法通过"));
            }
        }
    }
}

控制台输出:

> 通过限流
> 通过限流
> 无法通过
> 无法通过
> 无法通过
> 通过限流
> 无法通过
> 无法通过
> 无法通过
> 无法通过
> 通过限流
> 无法通过
> 无法通过
> 无法通过
> 无法通过
> 通过限流
> 无法通过
> 无法通过
> 无法通过
> 无法通过
> 通过限流
> 无法通过
> 无法通过
> 无法通过
> 无法通过
> 通过限流
> 无法通过

每隔4秒通过1次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值