基于codis实现分布式锁

文章目录

  • 初始化JedisResourcePool
  • 使用原子操作来申请与释放锁

初始化JedisResourcePool

RoundRobinJedisPool会 轮流连接当前可用的codisProxy

	@ConfigurationProperties(prefix = "codis")
	public class CodisProperties {
		String zookeeper;
		String zkProxyDir;
		String authpassword;

		@Bean
		public JedisResourcePool jodisPoolFactory() {
			return RoundRobinJedisPool.create().curatorClient(this.zookeeper, 30000).timeoutMs(2000)
					.zkProxyDir(this.zkProxyDir).password(this.authpassword).build();
		}

		public String getZookeeper() {
			return zookeeper;
		}

		public void setZookeeper(String zookeeper) {
			this.zookeeper = zookeeper;
		}

		public String getZkProxyDir() {
			return zkProxyDir;
		}

		public void setZkProxyDir(String zkProxyDir) {
			this.zkProxyDir= zkProxyDir;
		}

		public String getAuthpassword() {
			return authpassword;
		}

		public void setAuthpassword(String authpassword) {
			this.authpassword = authpassword;
		}
	}

 

使用原子操作来申请与释放锁

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.codis.jodis.JedisResourcePool;
import redis.clients.jedis.Jedis;

public class CodisDistributedLock {

	private static Logger LOGGER = LoggerFactory.getLogger(CodisDistributedLock.class);
	private final JedisResourcePool codisPool;
	private final String redisKey;
	private String redisValue;

	/** 默认超时时间, 单位秒 */
	private long TIMEOUT_SECOND = 30;

	/**
	 * 构造函数
	 */
	public CodisDistributedLock(JedisResourcePool pool, String lockName) {
		this.codisPool = pool;
		this.redisKey = lockName;
	}

	/**
	 * 获得分布式锁
	 * 
	 * @param key
	 * @return true:获得锁成功, false:获得锁失败
	 */
	public boolean getLock() {
		return lock(TIMEOUT_SECOND);
	}

	/**
	 * 获得分布式锁
	 * 
	 * @param key
	 * @param timeoutSeconds 客户端异常退出后,锁的自动超时时间
	 * @return true:获得锁成功, false:获得锁失败
	 */
	public boolean getLock(long timeoutSeconds) {
		if (timeoutSeconds <= 0) {
			return false;
		}
		return lock(timeoutSeconds);
	}

	private boolean lock(long timeoutSeconds) {
		boolean flag = false;
		try {
			this.redisValue = java.util.UUID.randomUUID().toString();
			String status = "";
			try (Jedis jedis = codisPool.getResource()) {
				// 原子操作:如果不存在锁,则设置锁
				status = jedis.set(redisKey, this.redisValue, "NX", "EX", timeoutSeconds);
			}
			if ("OK".equals(status)) {
				flag = true;
			}
		} catch (Exception ex) {
			LOGGER.error("get lock failed:" + redisKey, ex);
		}
		return flag;
	}

	/**
	 * 释放锁
	 * 
	 * @throws DistributedLockException
	 */
	public void releaseLock() {
		// 在releaseLock前,存在由于锁自动过期,而被其他线程获取同一个锁的可能;
		// 保证锁的自动过期时间大于任务最大执行时间,来避免此情况
		// 最好是用关系数据库行锁或zookeeper的长连接来维持锁(它们都是基于session来维持锁)
		try {
			Long result = 0L;
			// 原子操作:如果锁的值与当前值相等,则删除锁
			String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
			try (Jedis jedis = codisPool.getResource()) {
				// Codis会将这个脚本分配到参数列表中的第一个key所在的redis上执行
				result = (Long) jedis.eval(script, Arrays.asList(redisKey), Arrays.asList(redisValue));
			}
			if (result == null || result != 1L) {
				LOGGER.error("release lock failed:" + this.redisKey + " 已自动超时,可能已被其他线程重新获取锁");
			}
		} catch (Exception ex) {
			LOGGER.error("release lock failed:" + this.redisKey, ex);
		}
	}
}

你可能感兴趣的:(java)