背景
基于redis实现。
代码
package xxx.trade.util;import xxx.core.exception.BizException;import org.apache.commons.lang.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import redis.clients.jedis.JedisCluster;import java.util.Collections;public class JedisUtil { private static final Logger LOGGER = LoggerFactory .getLogger(JedisUtil.class); private static JedisCluster jedisCluster; private static final String PREFIX="xxx-callback:"; public JedisUtil() { // do nothing } public static boolean lock(String key , String value , String nxx, Long lockExpireTimeOut) { if (StringUtils.isBlank(key)) { throw new BizException("key must not null!"); } else { LOGGER.info("JedisTemplate:get cache key={},value={}", key, value); String result = jedisCluster.set(PREFIX+key ,value,nxx,"EX",lockExpireTimeOut); if ("OK".equals(result)) { return true; } return false; } } public static boolean unlock(String key, String value) { try { String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedisCluster.eval(luaScript, Collections.singletonList(PREFIX + key), Collections.singletonList(value)); if (!"0".equals(result.toString())) { return true; } } catch (Exception ex) { LOGGER.error("unlock error"); } return false; } static { ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"conf/springconf/redis/redis-spring-context.xml"}); jedisCluster = (JedisCluster)context.getBean("jedisClusterConfigA"); }}
应用
次要是两步
1.获取锁
2.开释锁
代码
try{ 获取锁; 解决业务;} finally{ 开释锁;}
外围
是基于redis的set命令。
超时
为什么要设置超时?因为怕断电这种状况,导致获取锁之后,始终没有开释,导致其余的服务都获取不了锁。
断电之后,开释锁的代码,就没执行。
lua
为什么要用Lua,因为是lua脚本原子操作。
而删除的时候,蕴含两步
1.先读 //先测验redis是否有数据
2.后写 //如果没有,就删除数据
所以,开释锁的代码应用了lua脚本来确保两步操作的原子性。