只在键
key
不存在的情况下, 将键key
的值设置为value
。若键
key
已经存在, 则SETNX
命令不做任何动作。
SETNX
是『SET if Not eXists』(如果不存在,则 SET)的简写。
命令在设置成功时返回 1
, 设置失败时返回 0
。
redis> EXISTS job # job 不存在
(integer) 0
redis> SETNX job "programmer" # job 设置成功
(integer) 1
redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败
(integer) 0
redis> GET job # 没有被覆盖
"programmer"
当同一个请求在短时间内重复提交时,容易导致系统不稳定、数据库连接池占用大。例如,一个下载数据的请求在执行过程中,由于下载的数据量大、耗时较长。当客户端通过刷新或者再次点击下载操作触发下载请求时,就会导致请求重复提交。
使用redis将同一个请求的关键信息作为key
存在redis中,并设置key
的有效时间,当请求执行完成后主动销毁这个key
。如果前一次的请求还在执行过程中,后面的重复请求在执行时,先通过setnx
检查key
是否存在(前一个请求是否执行完毕)。如果key
存在(前一次请求还没有执行完毕),则返回key
的剩余有效时间。如果key
不存在(前一次已经执行完毕),则新建key
重新执行请求。
//通过redis处理数据重复下载问题
@RequestMapping(value="/common",method=RequestMethod.POST)
@ResponseBody
public ResultData downCommon(String beamcodes) {
//请求的关键信息key
Integer bfid = getLoginBfId();
String key = SystemConfigVal.REDIS_DEFAULT_KEY + ZL_CACHE_PREFIX + bfid + "&" + beamcodes;
String value = getLoginBfId() + "&" + beamcodes;
//key的有效时间60s
Long seconds = 60L;
//通过setnx检查key是否存在,不存在时才执行下载请求
Long result = redisUtil.setnx(key,seconds.intValue(),value);
if(result == 1){
//执行数据下载,数据下载成功后删除key
tensionDownService.downTensionData(bfid,beamcodes,key);
}
//返回key的有效时间
seconds = redisUtil.ttl(key);
return toSuccess(seconds==null?0L:seconds);
}
当多个请求并发执行时,容易导致系统不稳定、数据异常。例如,在执行下载数据请求时,需要先获取token口令,每次下载都需要获取最新的token。当下载数据请求并发时,多次请求获取的token不一致,导致数据下载的token认证失败。
使用redis将获取的最新token存在redis中,并设置key
的有效时间。当进行数据下载时,先检查redis中的token是否存在,优先使用redis中的token,如果redis中token不存在或者已经失效,则重新获取最新的token并同步存进redis中。如果多个请求并发下载数据时,并且redis中不存在token,通过setnx
设置最新的token。第一个setnx
成功的请求将token更新至redis中,其他未setnx
成功的请求则获取redis中的最新token(并发时,由于redis中token创建和获取有时间差,未setnx
成功的请求需要多次才能获取到redis中的token)。
//通过redis处理数据并发下载问题
@Override
public void downTensionData(Integer bfid, String beamcodes, String key) {
try{
String token = null;
//请求的关键信息key
String tokenKey = SystemConfigVal.REDIS_DEFAULT_KEY + ZL_CACHE_PREFIX + bfid;
//并发请求时通过setnx抢占key,抢占成功的请求更新token,抢占失败的请求获取最新的token
Long result = redisUtil.setnx(tokenKey,60*30,"");
if(result == 1){
//当redis不存在token时,将获取到最新token更新至redis中
token = tensionAuthService.getToken(bfid);
redisUtil.set(tokenKey,60*30,token);
}else if(result == 0){
//当redis存在token时,获取token,直至获取到到有效token
for (int i = 0; i < 60; i++) {
Thread.sleep(1000);
token = redisUtil.getString(tokenKey);
if(!StringUtils.isEmpty(token)){
break;
}
}
}
if(token!=null && !"".equals(token.trim())) {
//执行数据下载
Map<String, Object> authUser = tensionAuthService.getAuthUser(bfid);
tensionDownService.downloadTensionCommon(token,String.valueOf(authUser.get("account")),bfid, beamcodes);
}
}catch (Exception e){
log.error(e.getMessage());
}finally {
redisUtil.delete(key);
}
}
RedisUtil
工具类import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.SerializationUtils;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import javax.annotation.PostConstruct;
/**
* @author jiangdongping
* @version v1.0
* @ClassName: RedisUtils
* @date 2021/1/6 9:21
*/
@Component
public class RedisUtil {
private Log log = LogFactory.getLog(RedisUtil.class);
@Autowired
private ShardedJedisPool shardedJedisPool;
private static boolean isEnabled = true;
/**
* @param shardedJedisPool the shardedJedisPool to set
*/
public void setShardedJedisPool(ShardedJedisPool shardedJedisPool) {
this.shardedJedisPool = shardedJedisPool;
}
@PostConstruct
private void checkConnect() {
try {
log.info("系统启动,检查Redis服务是否启动!");
ShardedJedis jedis = shardedJedisPool.getResource();
isEnabled = jedis!=null?true:false;
log.info("Redis服务已启动!");
}catch (Exception e) {
log.error("redis 连接失败:"+e.getMessage());
isEnabled = false;
}
}
public static boolean isEnable() {
return isEnabled;
}
public boolean valiEnable() {
//如果启用状态为false,主动尝试一次连接,更新状态
if(!isEnabled) {
try {
log.info("系统启动,检查Redis服务是否启动!");
ShardedJedis jedis = shardedJedisPool.getResource();
isEnabled = (jedis!=null?true:false);
log.info("Redis服务已启动!");
}catch (Exception e) {
log.error("redis 连接失败:"+e.getMessage());
isEnabled = false;
}
}
return isEnabled;
}
public String set(String key, int seconds, String value) {
if(!valiEnable()) {
return null;
}
try {
ShardedJedis jedis = shardedJedisPool.getResource();
String result = jedis.setex(SerializationUtils.serialize(key), seconds,SerializationUtils.serialize(value));
jedis.close();
return result;
} catch (Exception e) {
e.printStackTrace();
isEnabled = false;
log.error("redis 连接失败:"+e.getMessage());
}
return null;
}
public Long setnx(String key, int seconds, String value) {
if(!valiEnable()) {
return null;
}
try {
ShardedJedis jedis = shardedJedisPool.getResource();
Long result = jedis.setnx(SerializationUtils.serialize(key),SerializationUtils.serialize(value));
if(result == 1){
jedis.expire(SerializationUtils.serialize(key),seconds);
}
jedis.close();
return result;
} catch (Exception e) {
e.printStackTrace();
isEnabled = false;
log.error("redis 连接失败:"+e.getMessage());
}
return null;
}
public String getString(String key) {
if(!valiEnable()) {
return null;
}
if(!exists(key)) {
log.info("该值:"+key +" 在缓存中不存在!");
return null;
}
try {
ShardedJedis jedis = shardedJedisPool.getResource();
byte[] rb = jedis.get(SerializationUtils.serialize(key));
jedis.close();
return SerializationUtils.deserialize(rb).toString();
} catch (Exception e) {
isEnabled = false;
log.error("redis 连接失败:"+e.getMessage());
return null;
}
}
public boolean exists(String key) {
if(!valiEnable()) {
return false;
}
try {
ShardedJedis jedis = shardedJedisPool.getResource();
boolean result = jedis.exists(SerializationUtils.serialize(key));
jedis.close();
return result;
} catch (Exception e) {
e.printStackTrace();
isEnabled = false;
log.error("redis 连接失败:"+e.getMessage());
return false;
}
}
public void delete(String key) {
if(!valiEnable()) {
return;
}
if(!exists(key)) {
log.info("该值:"+key +" 在缓存中不存在!");
return;
}
try {
ShardedJedis jedis = shardedJedisPool.getResource();
jedis.del(SerializationUtils.serialize(key));
jedis.close();
} catch (Exception e) {
e.printStackTrace();
isEnabled = false;
log.error("redis 连接失败:"+e.getMessage());
}
}
public Long ttl(String key) {
if(!valiEnable()) {
return null;
}
if(!exists(key)) {
log.info("该值:"+key +" 在缓存中不存在!");
return null;
}
try {
ShardedJedis jedis = shardedJedisPool.getResource();
Long seconds = jedis.ttl(SerializationUtils.serialize(key));
jedis.close();
return seconds;
} catch (Exception e) {
isEnabled = false;
log.error("redis 连接失败:"+e.getMessage());
return null;
}
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。