spring boot專案中redis分散式鎖實現 程式碼模板
阿新 • • 發佈:2019-02-09
1,在application.properties中配置redis主機:
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=XXX
2,新增redis配置檔案:cache/RadisLock.java
import org.springframework.data.redis.core.RedisTemplate; public class RedisLock { private RedisTemplate<String, String> redisTemplate; /** * 重試時間 */ private static final int DEFAULT_ACQUIRY_RETRY_MILLIS = 100; /** * 鎖的字尾 */ private static final String LOCK_SUFFIX = "_redis_lock"; /** * 鎖的key */ private String lockKey; /** * 鎖超時時間,防止執行緒在入鎖以後,防止阻塞後面的執行緒無法獲取鎖 */ private int expireMsecs = 60 * 1000; /** * 執行緒獲取鎖的等待時間 */ private int timeoutMsecs = 10 * 1000; /** * 是否鎖定標誌 */ private volatile boolean locked = false; /** * 構造器 * * @param redisTemplate * @param lockKey 鎖的key */ public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey){ this.redisTemplate = redisTemplate; this.lockKey = lockKey + LOCK_SUFFIX; } /** * 構造器 * * @param redisTemplate * @param lockKey 鎖的key * @param timeoutMsecs 獲取鎖的超時時間 */ public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey, int timeoutMsecs){ this(redisTemplate, lockKey); this.timeoutMsecs = timeoutMsecs; } /** * 構造器 * * @param redisTemplate * @param lockKey 鎖的key * @param timeoutMsecs 獲取鎖的超時時間 * @param expireMsecs 鎖的有效期 */ public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey, int timeoutMsecs, int expireMsecs){ this(redisTemplate, lockKey, timeoutMsecs); this.expireMsecs = expireMsecs; } public String getLockKey() { return lockKey; } /** * 封裝和jedis方法 * * @param key * @return */ private String get(final String key) { Object obj = redisTemplate.opsForValue().get(key); return obj != null ? obj.toString() : null; } /** * 封裝和jedis方法 * * @param key * @param value * @return */ private boolean setNX(final String key, final String value) { return redisTemplate.opsForValue().setIfAbsent(key, value); } /** * 封裝和jedis方法 * * @param key * @param value * @return */ private String getSet(final String key, final String value) { Object obj = redisTemplate.opsForValue().getAndSet(key, value); return obj != null ? (String) obj : null; } /** * 獲取鎖 * * @return 獲取鎖成功返回ture,超時返回false * @throws InterruptedException */ public boolean lock() throws InterruptedException { int timeout = timeoutMsecs; while (timeout >= 0) { long expires = System.currentTimeMillis() + expireMsecs + 1; String expiresStr = String.valueOf(expires); // 鎖到期時間 if (this.setNX(lockKey, expiresStr)) { locked = true; return true; } // redis裡key的時間 String currentValue = this.get(lockKey); // 判斷鎖是否已經過期,過期則重新設定並獲取 if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) { // 設定鎖並返回舊值 String oldValue = this.getSet(lockKey, expiresStr); // 比較鎖的時間,如果不一致則可能是其他鎖已經修改了值並獲取 if (oldValue != null && oldValue.equals(currentValue)) { locked = true; return true; } } timeout -= DEFAULT_ACQUIRY_RETRY_MILLIS; // 延時 Thread.sleep(DEFAULT_ACQUIRY_RETRY_MILLIS); } return false; } /** * 釋放獲取到的鎖 */ public void unlock() { if (locked) { redisTemplate.delete(lockKey); locked = false; } } }
3,在程式碼中使用redis:
/** * 新建sdk組織 * * @param request * @param result */ @RequestMapping(value = "/sdk", method = RequestMethod.POST) public ResponseResult createBizSDKGroups(HttpServletRequest request, @RequestBody @Validated final SdkGroupAO sdkGroupAO, BindingResult result) { ResultDelegate delegate = new ResultDelegate() { @Override public Object getResultObject() { RedisLock redisLock = new RedisLock(redisTemplate, "create#sdkgroup#" + sessionUser); try { if (redisLock.lock()) { try { bizGroupService.createBizSDKGroup(sessionUser, sdkGroupAO); } finally { redisLock.unlock(); } } else { return "請不要多次點選"; } } catch (InterruptedException e) { return "建立失敗"; } return 1; } }; return getResponseResult(request, delegate, result); }