1. 程式人生 > >使用RedisTemplate實現簡易的分散式鎖(僅供參考)

使用RedisTemplate實現簡易的分散式鎖(僅供參考)

package com.*.lock;

import lombok.extern.log4j.Log4j2;
import org.nutz.lang.Strings;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.Jedis;

import java.util.Collections;
import java.util.Objects;
import java.util.UUID;

/**
 * @author
yangxing * @since 2018-09-14 10:19 **/
@Log4j2 public class RedisLock { // 鎖標誌 public static final String PREFIX = "lock_"; // SET_IF_NOT_EXIST 標誌,表示如果當key不存在時進行set操作,否則不處理 private static final String NX = "NX"; // 過期時間標誌, EX 表示以秒作為單位 private static final String EX = "EX"; // 鎖成功標誌
private static final String LOCK_SUCCESS = "OK"; // 解鎖成功標誌 private static final long DEL_SUCCESS = 1L; // 預設過期時間 private static final int DEFAULT_EXPIRE_TIME = 60; // 預設重試次數 private static final int DEFAULT_RETRY_TIME = 3; // 預設重試暫停時間 private static final int DEFAULT_INTERNAL_TIME = 100
; // 解鎖時的 lua語言Script private static final String DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; private RedisTemplate<String, Object> redisTemplate; // 過期時間 private int expireSeconds; // 鎖重試次數 private int retryTimes; // 鎖重試間隔 private int internalTime; // 加鎖者標誌, 解鎖時只有與加鎖時的值一致才允許解鎖 private final String lockId; private RedisLock(RedisTemplate<String, Object> redisTemplate) { this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME); } private RedisLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) { this.redisTemplate = redisTemplate; this.expireSeconds = expireSeconds; this.retryTimes = retryTimes; this.internalTime = internalTime; this.lockId = UUID.randomUUID().toString(); } public boolean lock(String key) { if (Strings.isBlank(key)) { return false; } final String fullKey = PREFIX + key; log.debug("嘗試獲取鎖, 鎖Key:{}, 鎖標識:{}", fullKey, lockId); for (int i = 0; i < retryTimes; i++) { log.debug("第 {} 次嘗試獲取鎖", i + 1); String status = redisTemplate.execute((RedisCallback<String>) connection -> { String result; try (Jedis jedis = (Jedis) connection.getNativeConnection()) { result = jedis.set(fullKey, lockId, NX, EX, expireSeconds); } return result; }); if (LOCK_SUCCESS.equalsIgnoreCase(status)) { log.debug("獲取鎖成功, 鎖Key:{}, 鎖標識:{}", fullKey, lockId); return true; } try { Thread.sleep(internalTime); } catch (InterruptedException e) { break; } } log.debug("嘗試獲取鎖 {} 次後失敗, 鎖Key:{}", retryTimes, fullKey); return false; } public boolean unlock(String key) { if (Strings.isBlank(key)) { return false; } final String fullKey = PREFIX + key; log.debug("嘗試解鎖, 鎖Key:{}, 鎖標識:{}", fullKey, lockId); Object value = redisTemplate.execute((RedisCallback<Object>) connection -> { Object result; try (Jedis jedis = (Jedis) connection.getNativeConnection()) { result = jedis.eval(DEL_SCRIPT, Collections.singletonList(fullKey), Collections.singletonList(lockId)); } return result; }); boolean isUnlock = Objects.nonNull(value) && value instanceof Long && DEL_SUCCESS == (Long) value; if (isUnlock) { log.debug("解鎖成功, 鎖Key:{}", fullKey); } return isUnlock; } public String getLockId() { return lockId; } public static RedisLock getLock(RedisTemplate<String, Object> redisTemplate) { return new RedisLock(redisTemplate); } public static RedisLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds) { return new RedisLock(redisTemplate, expireSeconds, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME); } }