Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。一个完整的 Lua解释器不过200k,在目前所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式 脚本的最佳选择。
spring-boot项目,加入spring-boot-starter-data-redis依赖
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.5.RELEASEversion>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
dependencies>
在maven build中一定要加入关于脚本文件的resource,将lua脚本打包到classes目录中,如下
<build>
<resources>
<resource>
<directory>${basedir}/src/main/javadirectory>
<includes>
<include>**/*.luainclude>
includes>
resource>
resources>
build>
目录结构src/main/java/demo/script/xx.lua
使用redis中setnx对key进行加锁
local expire=tonumber(ARGV[2])
local ret=redis.call('set',KEYS[1],ARGV[1],'NX','PX',expire)
local strret=tostring(ret)
-- 用于查看结果,获取锁成功后返回随机结婚"table: 0x55d040f2edc0",否则返回false
redis.call('set','result',strret)
if strret == 'false' then
return false
else
return true
end
lock.lua
KEYS,ARGV关键字的意思:
KEYS[n]表示传入参数key主键中第一个主键值,0表示没有,可以传入多个,KEYS[2]传入的第二个主键
ARGV[n]表示传入的参数值,同样可以传入多个
解锁脚本
redis.call('del','result')
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end
unlock.lua
将封装好的lua脚本加入Spring容器中,方便调用
@Configuration
public class RedisScriptConfig {
@Bean
public RedisScript<Boolean> lockScript(){
RedisScript<Boolean> redisScript=null;
try {
ScriptSource scriptSource=new ResourceScriptSource(new ClassPathResource("demo\\script\\lock.lua"));
redisScript = RedisScript.of(scriptSource.getScriptAsString(), Boolean.class);
} catch (IOException e) {
e.printStackTrace();
}
return redisScript;
}
@Bean
public RedisScript<Long> unlockScript(){
RedisScript<Long> redisScript=null;
try {
ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("demo\\script\\unlock.lua"));
redisScript=RedisScript.of(scriptSource.getScriptAsString(),Long.class);
} catch (IOException e) {
e.printStackTrace();
}
return redisScript;
}
}
@RestController
public class RedisLuaController {
private final Log logger= LogFactory.getLog(RedisLuaController.class);
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Autowired
RedisScript<Boolean> lockScript;
@Autowired
RedisScript<Long> unlockScript;
@RequestMapping("distrLock/{key}/{uuid}")
public void Lock(@PathVariable String key,@PathVariable String uuid){
try {
Boolean flag = redisTemplate.execute(lockScript, Collections.singletonList(key), uuid, "50000");
logger.info("locked:{}"+flag);
} catch (Exception e) {
e.printStackTrace();
}
}
@RequestMapping("distrUnlock/{key}/{uuid}")
public void unlock(@PathVariable String key,@PathVariable String uuid){
try {
Long unlocked = redisTemplate.execute(unlockScript, Collections.singletonList(key), uuid);
logger.info("unlock status "+unlocked.toString());
} catch (Exception e) {
logger.error("解锁失败"+e.getMessage());
}
}
}
lua脚本在redis中体现的原子性大大为一些复杂操作提供了事务性保证,二是减少了网络开销和请求次数。这两点笔者认为最为重要。