水煮Redisson(十二)-指令集和命令转换器

174 阅读2分钟

前言

前面介绍Redisson锁的时候,忽略了一个细节,就是在lua脚本中,获取锁的时候,返回的nil,而在lua语义中,nil应该是false或者空,为什么不返回true呢?

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        internalLockLeaseTime = unit.toMillis(leaseTime);

    return evalWriteAsync(getName(), LongCodec.INSTANCE, command,
            "if (redis.call('exists', KEYS[1]) == 0) then " +
                    "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
			// 客户端线程成功获取锁,lua脚本返回nil。
                    "return nil; " +
                    "end; " +
                    "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                    "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "return nil; " +
                    "end; " +
                    "return redis.call('pttl', KEYS[1]);",
            Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}

LUA数值分析

www.runoob.com/lua/lua-dat…
来看看boolean的语义说明:
boolean类型只有两个可选值:true(真) 和 false(假),Lua 把 false 和 nil 看作是 false,其他的都为 true,数字 0 也是 true:

测试脚本

print(type(true))
print(type(false))
print(type(nil))

function isTrueOrFalse(value)
if value then
print("数值:",value,"是 true")
else
print("数值:",value,"是 false")
end
end

print(isTrueOrFalse(0))
print(isTrueOrFalse(-1))
print(isTrueOrFalse(1))
print(isTrueOrFalse(2))
print(isTrueOrFalse(nil))
print(isTrueOrFalse(true))
print(isTrueOrFalse(false))
print(isTrueOrFalse(""))

输出:---------------------
boolean
boolean
nil
数值: 0 是 true
数值: -1 是 true
数值: 1 是 true
数值: 2 是 true
数值: nil 是 false
数值: true 是 true
数值: false 是 false
数值: 是 true

Redisson指令集

RedisCommands接口中定义了一系列的脚本指令,其中就有前面提到的EVAL_NULL_BOOLEAN,EVAL_LONG

RedisStrictCommand<Boolean> EVAL_NULL_BOOLEAN = new RedisStrictCommand<Boolean>("EVAL", new BooleanNullReplayConvertor());
RedisStrictCommand<Long> EVAL_LONG = new RedisStrictCommand<Long>("EVAL");

EVAL_NULL_BOOLEAN

可以看到EVAL_LONG指令没有指定转换器,而EVAL_NULL_BOOLEAN对应的转换器为BooleanNullReplayConvertor,源码如下:

/**
 * 
 * @author Nikita Koksharov
 *
 */
public class BooleanNullReplayConvertor implements Convertor<Boolean> {

    @Override
    public Boolean convert(Object obj) {
        return obj == null;
    }

}

从转化程序来看,只有脚本返回null时【nil】,方法才能返回true。

EVAL_BOOLEAN

再看看EVAL_BOOLEAN,对应的转换器为BooleanReplayConvertor

public class BooleanReplayConvertor implements Convertor<Boolean> {

    @Override
    public Boolean convert(Object obj) {
        if (obj == null) {
            return null;
        }
        return Long.valueOf(1).equals(obj) || "OK".equals(obj);
    }

}

也就是说,我们指定脚本的RedisStrictCommand为EVAL_BOOLEAN时,当方法需要返回true时,脚本里要返回1或者OK才可以。