springboot2.x redis lettuce實現分佈式鎖

我查了不少redis分佈式鎖都是經過jedisclient來實現的,由於springboot2.x默認鏈接客戶端是lettuce,因此引用一個大佬用jedis寫的鎖修改下。


直接貼代碼redis

1.接口類
spring

public interface DistributedLock {
	
	public static final long TIMEOUT_MILLIS = 30000;
	
	public static final int RETRY_TIMES = 10;
	
	public static final long SLEEP_MILLIS = 500;
	
	public boolean lock(String key);
	
	public boolean lock(String key, int retryTimes);
	
	public boolean lock(String key, int retryTimes, long sleepMillis);
	
	public boolean lock(String key, long expire);
	
	public boolean lock(String key, long expire, int retryTimes);
	
	public boolean lock(String key, long expire, int retryTimes, long sleepMillis);
	
	public boolean releaseLock(String key);
}
複製代碼

2.依然是接口springboot

public abstract class AbstractDistributedLock implements DistributedLock {

	@Override
	public boolean lock(String key) {
		return lock(key , TIMEOUT_MILLIS, RETRY_TIMES, SLEEP_MILLIS);
	}

	@Override
	public boolean lock(String key, int retryTimes) {
		return lock(key, TIMEOUT_MILLIS, retryTimes, SLEEP_MILLIS);
	}

	@Override
	public boolean lock(String key, int retryTimes, long sleepMillis) {
		return lock(key, TIMEOUT_MILLIS, retryTimes, sleepMillis);
	}

	@Override
	public boolean lock(String key, long expire) {
		return lock(key, expire, RETRY_TIMES, SLEEP_MILLIS);
	}

	@Override
	public boolean lock(String key, long expire, int retryTimes) {
		return lock(key, expire, retryTimes, SLEEP_MILLIS);
	}

}複製代碼

3.實現類bash

@Component
public class RedisDistributedLock extends AbstractDistributedLock {
	
	@Autowired
	@Resource(name="redisTemplateMaster")
	private RedisTemplate<Object,Object> redisTemplate;
	
	private final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);
	
	//private RedisTemplate<Object, Object> redisTemplate;
	
	private ThreadLocal<String> lockFlag = new ThreadLocal<String>();
	
	public static final String UNLOCK_LUA;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append(" return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append(" return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }

    
    public RedisDistributedLock() {
		super();
	}

//	public RedisDistributedLock(RedisTemplate<Object, Object> redisTemplate) {
//		super();
//		this.redisTemplate = redisTemplate;
//	}

	@Override
	public boolean lock(String key, long expire, int retryTimes, long sleepMillis) {
		boolean result = setRedis(key, expire);
		// 若是獲取鎖失敗,按照傳入的重試次數進行重試
		while((!result) && retryTimes-- > 0){
			try {
				logger.debug("lock failed, retrying..." + retryTimes);
				Thread.sleep(sleepMillis);
			} catch (InterruptedException e) {
				return false;
			}
			result = setRedis(key , expire);
		}
		return result;
	}
	
	private boolean setRedis(final String key, final long expire ) {
		try{
			
            RedisCallback<Boolean> callback = (connection) -> {
            	String uuid = UUID.randomUUID().toString();
            	lockFlag.set(uuid);
                return connection.set(key.getBytes(Charset.forName("UTF-8")), uuid.getBytes(Charset.forName("UTF-8")), Expiration.milliseconds(expire), RedisStringCommands.SetOption.SET_IF_ABSENT);
            };
            return (Boolean)redisTemplate.execute(callback);
        } catch (Exception e) {
        	logger.error("redis lock error.", e);
        }
		return false;
	}
	
	
	@Override
	public boolean releaseLock(String key) {
		// 釋放鎖的時候,有可能由於持鎖以後方法執行時間大於鎖的有效期,此時有可能已經被另一個線程持有鎖,因此不能直接刪除
		try {
			 RedisCallback<Boolean> callback = (connection) -> {
			    String value = lockFlag.get();
	            return connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN ,1, key.getBytes(Charset.forName("UTF-8")), value.getBytes(Charset.forName("UTF-8")));
	        };
		     return (Boolean)redisTemplate.execute(callback);
		} catch (Exception e) {
			logger.error("release lock occured an exception", e);
		} finally {
			// 清除掉ThreadLocal中的數據,避免內存溢出
			lockFlag.remove();
		}
		return false;
	}
	
}
複製代碼
相關文章
相關標籤/搜索