基于redis实现分布式锁

257 阅读1分钟

背景

基于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脚本来确保两步操作的原子性。