在以往的单机系统中,为了做到线程安全,我们可以使用jdk提供给我们的synchronized关键字,也可以使用juc包下面的Lock,但是在现在流程的SOA服务中,显然就没法用了,毕竟服务器再也不是同一台了,你管得了自己,管不了别人。
有问题,那就会有解决方案,那就是分布式锁,通常利用Redis或者Zookeeper来实现,亦或者是利用三方框架来解决(redisson),今天来教大家用redis实现一个超级简单的分布式锁。
实现思路:setnx + lua
setNx:全名“set if not exist”,如果key不存在就set,如果key已存在就不set;
LUA:lua 脚本功能是Reids 2.6版本的最大亮点,通过内嵌对 Lua 环境的支持,Redis解决了长久以来不能高效地处理CAS(check-and-set)命令的缺点;
废话不多说,直接上代码了
/**
* set lock in redis, setNx(SET if Not eXists)
*
* @param key key
* @param value value
* @param expire second
* @return result(0 - false, 1 - true)
*/
public Long setLock(String key, String value, Integer expire) {
return redisTemplateJDKSerialization.execute((RedisCallback<Long>) connection -> {
StringRedisSerializer serializer = new StringRedisSerializer();
StringBuilder lua = new StringBuilder();
lua.append("local key = KEYS[1]");
lua.append("local value = ARGV[1]");
lua.append("local ok = redis.call('setnx', key, value)");
if (expire != null && expire > 0) {
lua.append("local ttl = ARGV[2]");
lua.append("if ok == 1 then");
lua.append(" redis.call('expire', key, ttl)");
lua.append("end ");
lua.append("return ok");
return connection.eval(serializer.serialize(lua.toString()), ReturnType.INTEGER, 1, serializer.serialize(key), serializer.serialize(value), serializer.serialize(expire.toString()));
}
lua.append("return ok");
return connection.eval(serializer.serialize(lua.toString()), ReturnType.INTEGER, 1, serializer.serialize(key), serializer.serialize(value));
});
}
上述代码中,不用lua脚本也是可以的,但是用了lua脚本会有一个更好的效果:
1、多个命令一次性提交,减少开销;
2、将内部的所有命令封装为一个原子操作;
3、执行之后lua脚本会保存在redis中,可多次复用;
评论