用 redis 設計 分佈式鎖 (一)

項目開發中我們經常需要用到分佈式鎖,尤其是在集羣環境多臺服務器情況下,比如 接口請求、定時任務等業務環境中;

今天 我們先介紹一種 redis 分佈式鎖,在普通項目中就可以使用,代碼如下:

1、redis 加鎖和釋放鎖代碼如下:

package com.jy.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCommands;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * RedisTemplate工具類  通過註解(@Component)掃描此類 
 */
@Component
public class RedisUtil implements ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);

    private static RedisTemplate<Object, Object> redisTemplate;//redis 模板類

    private static ThreadLocal<String> lockFlag = new ThreadLocal<String>();

    public static final String UNLOCK_LUA;

   //redis 加鎖方法
    public static Boolean setNX(String key,long timeout) {
        String  value = String.valueOf(System.currentTimeMillis());//鎖的value 值設置
        Boolean isExit = false;
        try {
            //設置鎖的核心代碼
            isExit = redisTemplate.getConnectionFactory().getConnection().setNX(key.getBytes(), value.getBytes());
            /** 如果設置成功,要設置其過期時間 */
            if (isExit) {
                redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
                logger.info(key+":成功獲取到鎖");
                return isExit;
            }
        }catch (Exception e){
            logger.info(key+":獲取鎖失敗",e);
        }
        logger.info(key+":未獲取到鎖");
        return isExit;
    }


//釋放鎖的核心方法
    public static void releaseLock(String key ){
        try {
            redisTemplate.opsForValue().getOperations().delete(key);//直接刪除key
            logger.info(key+";鎖釋放成功");
        } catch (Exception ex) {
            logger.error(key+";鎖釋放失敗",ex);
        }
    }

     //通過springIOC容器取redis模板類
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        redisTemplate = applicationContext.getBean(RedisTemplate.class);
    }

}

2、redis 分佈式鎖就這麼簡單,但是!此種方案是有有坑的,在普通項目、併發量小、定時任務頻率低、服務穩定的的情況下勉強可以使用。分析一下原因:首先,該加鎖過程不能保證原子性,比如剛剛加鎖之後,還沒有來得及設置超時時間,服務出問題了,或者中間出現了異常代碼,那麼這個鎖沒有設置超時時間,就可能成爲死鎖(如果業務代碼中沒有成功釋放鎖)。

其次:關於釋放鎖,如果在多線程情況下,可能會把其他線程的鎖(同一個key : 鎖)釋放掉,這對業務來說是非常危險的。

下篇我將介紹另一種redis 分佈式鎖,能很好的彌補這種方案的漏洞,敬請期待!下篇地址:

https://blog.csdn.net/nandao158/article/details/105866418

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章