乐趣区

关于分布式锁:基于redis实现分布式锁

背景

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

退出移动版