lua语言是一个轻量级的脚本语言,可以嵌入其他语言中使用,调用宿主语言的功能。lua语法简单,小巧,源码一共才200多K,本身不会有太强的功能,很多的语言也支持lua语言,比如redis、Nginx
redis语言中完美嵌入了lua脚本功能,redis可以调用lua脚本中的api,lua脚本也可以调用redis中的命令
在redis中调用lua脚本,需要使用eval
指令
127.0.0.1:6379>eval "return 'hello'" 0
"hello"
调用lua脚本,动态传入参数,其中表达式script后面第一个参数nkey表示key的对应位置,后面的表示key和对应的参数argv
# script脚本后面,第一个参数1表示key为其后的第一个参数,也就是1,如何key后面的参数都是ARGV
127.0.0.1:6379>eval "if KEYS[1]=='1' then return ARGV[1] end return ARGV[2]" 1 1 'hello' 'hi'
"hello"
127.0.0.1:6379>eval "if KEYS[1]=='1' then return ARGV[1] end return ARGV[2]" 1 1 'hello' 'hi'
"hi"
使用lua调用redis的命令,需要使用redis.call
调用
# key为0表示能获取到锁
127.0.0.1:6379>eval "local key = redis.call('exists',KEYS[1]) if key==0 then return redis.call('set',KEYS[1],ARGV[1]) end return 1" 1 orderId01 1
写个lua脚本,来实现一个简单的分布锁锁
private static final String LOCK_LUA_SCRIPT = "local lockParam = redis.call('exists', KEYS[1])\n" +
"if lockParam == 0 then\n" +
"redis.call('set', KEYS[1], ARGV[1])\n" +
"redis.call('expire', KEYS[1], ARGV[2])\n" +
"end\n" +
"return lockParam\n";
简单实现抢单的业务
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testLua() {
Long orderId = IdUtil.getSnowflake().nextId();
String lockKey = "order:"+orderId;
String requestId = IdUtil.randomUUID();
try {
Long lock = (Long) redisTemplate.execute(RedisScript.of(LOCK_LUA_SCRIPT, Long.class), Arrays.asList(lockKey), requestId, 30);
// 抢得到锁
if (lock == 0) {
// 模拟业务执行10s
TimeUnit.MILLISECONDS.sleep(10*1000);
}
log.info("lock:[{}]", lock);
} catch (Exception e) {
testRelease(lockKey, requestId);
} finally {
testRelease(lockKey, requestId);
}
}
锁释放的,也通过lua脚本实现,主要是保证原子性
private String UNLOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
@Test
public void testRelease(String lockKey, String lockValue) {
redisTemplate.execute(RedisScript.of(UNLOCK_LUA_SCRIPT, Long.class), Arrays.asList(lockKey), lockValue);
}