redis 集羣實現分佈式鎖 兩種方式

第一種:使用setnx  關鍵在getShortMessageByRedis方法中

package com.oppo.baed.ids.service.service.impl;

import com.oppo.baed.ids.service.constant.BussinessConstats;
import com.oppo.baed.ids.service.domain.MessageRecord;
import com.oppo.baed.ids.service.service.RedisService;
import com.oppo.baed.ids.service.util.DateUtil;
import com.oppo.baed.ids.service.util.FastJsonConvertUtil;
import com.oppo.basic.heracles.client.core.spring.annotation.HeraclesDynamicConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;

import java.util.Collections;
import java.util.List;

/**
 * @description:Redis集羣緩存處理服務
 */
@Service
public class RedisServiceImpl implements RedisService {

    @Autowired
    private JedisCluster jedisCluster;

    //存儲短信記錄信息
    final static String SHORT_MESSAGE_RECORD_LIST = "short_message_reocrd_list";

    //用於實現分佈式key
    final static String SHORT_MESSAGE_DISTRIBUTED_KEY = "short_message_distributed_key";

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";


    Logger logger = LogManager.getLogger(RedisServiceImpl.class.getName());

    @HeraclesDynamicConfig(key = "spring.redis.prefix", fileName = "application.properties")
    @Value("${spring.redis.prefix}")
    private String prefix;

    @Override
    public Long delete(String key) {
        return jedisCluster.del(prefix + key);
    }

    @Override
    public String get(String key) {
        return jedisCluster.get(prefix + key);
    }

    @Override
    public String set(String key, String value, int ttl) {
        return jedisCluster.setex(prefix + key, ttl, value);
    }

    @Override
    public long rPush(String key,String content) {
        return jedisCluster.lpush(key,content);
    }

    @Override
    public List<String> getShortMessageByRedis(String key, long start, long end) {
        List<String> messgeItem = null;
        boolean exists =  jedisCluster.exists(SHORT_MESSAGE_DISTRIBUTED_KEY);
        /**
         * 使用過期key和setnx模擬實現分佈式鎖
         * key值存在 說明鎖被定義,直接退出
         */
        if(exists){//
            System.out.println("key 值 存在");
            return null;
        }
        else{
                /**
                 * key不值存在,使用setnx創建鎖key
                 */
                Long isSuccese =  jedisCluster.setnx(SHORT_MESSAGE_DISTRIBUTED_KEY,"xx");
                if(isSuccese==1){
                    //成功
                    System.out.println(Thread.currentThread().getName()+":鎖SHORT_MESSAGE_DISTRIBUTED_KEY創建完成");
                    //給鎖設置過期時間
                    jedisCluster.expire(SHORT_MESSAGE_DISTRIBUTED_KEY,10);
                    //獲取指定範圍集合元素
                    messgeItem  = jedisCluster.lrange(key,start,end);
                    //刪除已獲取的數據
                    jedisCluster.ltrim(key, end+1, -1);
                    //釋放鎖
                    jedisCluster.del(SHORT_MESSAGE_DISTRIBUTED_KEY);
                    System.out.println(Thread.currentThread().getName()+":釋放鎖 SHORT_MESSAGE_DISTRIBUTED_KEY");
                    return messgeItem;
                }else{
                    //失敗
                    System.out.println(Thread.currentThread().getName()+":setnx創建key:SHORT_MESSAGE_DISTRIBUTED_KEY衝突");
                    //高併發情況下,可能會出現創建已存在的key
                    return  null;
                }

        }
    }




    @Override
    public void addToRedisShortMessage(String mobile) {
        //記錄發送的記錄
        MessageRecord messageRecord = new MessageRecord();
        messageRecord.setMsgType(BussinessConstats.BussinessType.IDS.getBussType());
        String timeStr = String.valueOf(System.currentTimeMillis());
        timeStr = timeStr.substring(0,10);
        messageRecord.setSendTime(DateUtil.timeStamp2Date(timeStr,"yyyy-MM-dd HH:mm:ss"));
        messageRecord.setMobile(mobile);
        System.out.println(rPush(SHORT_MESSAGE_RECORD_LIST, FastJsonConvertUtil.convertObjectToJSON(messageRecord)));
    }

}

 

第二種:結合lua腳本,加鎖解鎖分別使用兩個方法實現

package com.oppo.baed.ids.service.service.impl;

import com.oppo.baed.ids.service.constant.BussinessConstats;
import com.oppo.baed.ids.service.domain.MessageRecord;
import com.oppo.baed.ids.service.service.RedisService;
import com.oppo.baed.ids.service.util.DateUtil;
import com.oppo.baed.ids.service.util.FastJsonConvertUtil;
import com.oppo.basic.heracles.client.core.spring.annotation.HeraclesDynamicConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;

import java.util.Collections;
import java.util.List;

/**
 * @description:Redis集羣緩存處理服務
 */
@Service
public class RedisServiceImpl implements RedisService {

    @Autowired
    private JedisCluster jedisCluster;

    //存儲短信記錄信息
    final static String SHORT_MESSAGE_RECORD_LIST = "short_message_reocrd_list";

    //用於實現分佈式key
    final static String SHORT_MESSAGE_DISTRIBUTED_KEY = "short_message_distributed_key";

    private static final String LOCK_SUCCESS = "OK";
    private static final Long RELEASE_SUCCESS = 1L;
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";


    Logger logger = LogManager.getLogger(RedisServiceImpl.class.getName());

    @HeraclesDynamicConfig(key = "spring.redis.prefix", fileName = "application.properties")
    @Value("${spring.redis.prefix}")
    private String prefix;

    @Override
    public Long delete(String key) {
        return jedisCluster.del(prefix + key);
    }

    @Override
    public String get(String key) {
        return jedisCluster.get(prefix + key);
    }

    @Override
    public String set(String key, String value, int ttl) {
        return jedisCluster.setex(prefix + key, ttl, value);
    }

    @Override
    public long lPush(String key,String content) {
        return jedisCluster.lpush(key,content);
    }

    @Override
    public List<String> getShortMessageByRedis(String key, long start, long end) {
        List<String> messgeItem = null;
        try {
            Boolean lock = lock(SHORT_MESSAGE_DISTRIBUTED_KEY,Thread.currentThread().getName(),20);
            if(lock){
                //獲取指定範圍集合元素
                messgeItem  = jedisCluster.lrange(key,start,end);
                //刪除已獲取的數據
                jedisCluster.ltrim(key, end+1, -1);
                return messgeItem;
            }
        }catch (Exception e){

        }finally {
            unlock(SHORT_MESSAGE_DISTRIBUTED_KEY,Thread.currentThread().getName());
        }
        return messgeItem;
    }


    public boolean lock(String key, String requestId, int ttl) {
        String result = jedisCluster.set(key, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, ttl);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    public boolean unlock(String key, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedisCluster.eval(script, Collections.singletonList(key), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }



    @Override
    public void addToRedisShortMessage(String mobile) {
        //記錄發送的記錄
        MessageRecord messageRecord = new MessageRecord();
        messageRecord.setMsgType(BussinessConstats.BussinessType.IDS.getBussType());
        String timeStr = String.valueOf(System.currentTimeMillis());
        timeStr = timeStr.substring(0,10);
        messageRecord.setSendTime(DateUtil.timeStamp2Date(timeStr,"yyyy-MM-dd HH:mm:ss"));
        messageRecord.setMobile(mobile);
        System.out.println(lPush(SHORT_MESSAGE_RECORD_LIST, FastJsonConvertUtil.convertObjectToJSON(messageRecord)));
    }

}

 

以上兩種方式是經過本人實測,也是公司項目中用到的。

建議使用第二種

 

 

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