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)));
    }

}

以上两种方式是经过本人实测,也是公司项目中用到的。

建议使用第二种

猜你喜欢

转载自blog.csdn.net/nameIsHG/article/details/89445704