第一種:使用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)));
}
}
以上兩種方式是經過本人實測,也是公司項目中用到的。
建議使用第二種