Redis lua实战 保证操作的原子性

背景:

      控制指定请求端的最大并发数;

使用redis+lua脚本来完成加锁,释放锁的原子性操作

/**
     * 执行脚本
     * @param keys
     * @param args
     * @return true 超过最大限制
     */
    public Boolean eval(String script, List<String> keys, List<String> args) {
         Long result = redisTemplate.execute(new RedisCallback<Long>() {
                                         public Long doInRedis(RedisConnection connection) throws DataAccessException {
                                             Object nativeConnection = connection.getNativeConnection();
                                             // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
                                             // 集群模式
                                             if (nativeConnection instanceof JedisCluster) {
                                                 return (Long) ((JedisCluster) nativeConnection).eval(script, keys, args);
                                             }
                                             return 0L;
                                         }
                                     });
        return result == 1L ? true : false;
    }

eval方法:执行redis cluster方式的代码;

/**
     * 最大并发lua脚本
     * @param key 需要拦截的key
     * @param value 参数:最大并发数
     * @return
     */
    public boolean evalIncr(String key, Integer value) {
        List<String> keys =  new ArrayList<>();
        keys.add(key);
        List<String> values = new ArrayList<>();
        values.add(String.valueOf(value));
        String srcipt = "local currCount = redis.call('incr', KEYS[1])\n" +
                "redis.call('expire', KEYS[1], 300)\n" +
                "if currCount > tonumber(ARGV[1]) then\n" +
                "    redis.call(\"decr\", KEYS[1])" +
                "    return 1\n" +
                "else\n" +
                "    return 0\n" +
                "end\n";
        return eval(srcipt, keys, values);
    }

evalIncr方法:当有客户端请求时,对此端并发数做incr操作,如果达到了最大并发数则返回1,为了防止程序崩溃等意外事件,对key设置过期时间,防止死锁;

 /**
     * 原子decr
     * @param key
     */
    public void evalDecr(String key) {
        List<String> keys =  new ArrayList<>();
        keys.add(key);
        List<String> values = new ArrayList<>();
        values.add("1");
        //get不到key时,redis返回显示的nil,实际上是boolean类型的 false
        String srcipt = "local currCount = redis.call('get', KEYS[1])\n" +
                "if (type(currCount) == 'boolean' and currCount == false) then\n" +
                "    return 0\n" +
                "elseif(tonumber(currCount) > 0) then\n" +
                "    redis.call(\"decr\", KEYS[1])" +
                "    return 1\n" +
                "else\n" +
                "    return 0\n" +
                "end\n";
        eval(srcipt, keys, values);
    }

evalDecr方法:释放占用的并发数,获取到redis中当前客户端已用的并发数;

if - 判断获取的值是否为nil ,如果是不做任何操作(这种情况可能是key自动到了过期时间)

elseif - 判断当前已用并发数是否大于0,大于0在decr,防止出现负数,因为redis获取出的值为string类型,所以使用tonumber方法转为数字在比较

else - 其他情况不做任何操作

srcipt为:lua语言

lua基础参考:http://www.runoob.com/lua/lua-tutorial.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值