*简介:RedisConnection实现分布锁的方式,采用redisTemplate操作redisConnection实现setnx和setex两个命令连用**
- redisTemplate本身有没通过valueOperation实现分布式锁
* 问题探索: Spring Data Redis提供了与Java客户端包的集成服务,比如Jedis, JRedis等 通过getNativeConnection的方式可以解决问题吗?
- Spring Data Redis提供了与Java客户端包的集成服务,比如Jedis, JRedis等
* 代码演示/**
* 重写redisTemplate的set方法
*
* 命令 SET resource-name anystring NX EX max-lock-time 是一种在 Redis 中实现锁的简单方法。
*
* 客户端执行以上的命令:
*
* 如果服务器返回 OK ,那么这个客户端获得锁。
* 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。
*
* @param key 锁的Key
* @param value 锁里面的值
* @param seconds 过去时间(秒)
* @return
*/
private String set(final String key, final String value, final long seconds) {
Assert.isTrue(!StringUtils.isEmpty(key), "key不能为空");
return redisTemplate.execute(new RedisCallback() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
Object nativeConnection = connection.getNativeConnection();
String result = null;
if (nativeConnection instanceof JedisCommands) {
result = ((JedisCommands) nativeConnection).set(key, value, NX, EX, seconds);
}
if (!StringUtils.isEmpty(lockKeyLog) && !StringUtils.isEmpty(result)) {
logger.info("获取锁{}的时间:{}", lockKeyLog, System.currentTimeMillis());
}
return result;
}
});
}
- 为什么新版本的spring-data-redis会报class not can not be case错误
io.lettuce.core.RedisAsyncCommandsImpl cannot be cast to redis.clients.jedis.JedisCommands
- 探索spring-data-redis升级
* 官网api分析 https://docs.spring.io/spring-data/redis/docs/1.5.0.RELEASE/api/ https://docs.spring.io/spring-data/redis/docs/2.0.13.RELEASE/api/
* 源码改造public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection(); return redisConnection.set(key.getBytes(), getHostIp().getBytes(), Expiration.seconds(expire), RedisStringCommands.SetOption.ifAbsent());
}
这样通过jedis源码改造后,同样实现了setnx,setex的连用效果
完整代码:package com.concurrent.schedule;
import com.concurrent.util.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/*****
* RedisConnection实现分布式锁
*/
@Component
public class JedisDistributedLock {
private final Logger logger = LoggerFactory.getLogger(JedisDistributedLock.class);
private static String LOCK_PREFIX = "JedisDistributedLock_";
private DefaultRedisScript lockScript;
@Resource
private RedisTemplate redisTemplate;
@Autowired
private RedisService redisService;
public static final String UNLOCK_LUA;
static {
StringBuilder sb = new StringBuilder();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redis.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
@Scheduled(cron = "0/10 * * * * *")
public void lockJob() {
String lock = LOCK_PREFIX + "JedisNxExJob";
boolean lockRet = false;
try {
lockRet = this.setLock(lock, 600);
//获取锁失败
if (!lockRet) {
String value = (String) redisService.genValue(lock);
//打印当前占用锁的服务器IP
logger.info("jedisLockJob get lock fail,lock belong to:{}", value);
return;
} else {
//获取锁成功
logger.info("jedisLockJob start lock lockNxExJob success");
Thread.sleep(5000);
}
} catch (Exception e) {
logger.error("jedisLockJob lock error", e);
} finally {
if (lockRet) {
logger.info("jedisLockJob release lock success");
releaseLock(lock,getHostIp());
}
}
}
public boolean setLock(String key, long expire) {
try {
Boolean result = redisTemplate.execute(new RedisCallback() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
return connection.set(key.getBytes(), getHostIp().getBytes(), Expiration.seconds(expire) ,RedisStringCommands.SetOption.ifAbsent());
}
});
return result;
} catch (Exception e) {
logger.error("set redis occured an exception", e);
}
return false;
}
/* public String get(String key) {
try {
RedisCallback callback = (connection) -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
return commands.get(key);
};
String result = redisTemplate.execute(callback);
return result;
} catch (Exception e) {
logger.error("get redis occured an exception", e);
}
return "";
}*/
/**
* 释放锁操作
* @param key
* @param value
* @return
*/
private boolean releaseLock(String key, String value) {
lockScript = new DefaultRedisScript();
lockScript.setScriptSource(
new ResourceScriptSource(new ClassPathResource("unlock.lua")));
lockScript.setResultType(Boolean.class);
// 封装参数
List keyList = new ArrayList();
keyList.add(key);
keyList.add(value);
Boolean result = (Boolean) redisTemplate.execute(lockScript, keyList);
return result;
}
/**
* 获取本机内网IP地址方法
*
* @return
*/
private static String getHostIp() {
try {
Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
while (allNetInterfaces.hasMoreElements()) {
NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
Enumeration addresses = netInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress ip = (InetAddress) addresses.nextElement();
if (ip != null
&& ip instanceof Inet4Address
&& !ip.isLoopbackAddress() //loopback地址即本机地址,IPv4的loopback范围是127.0.0.0 ~ 127.255.255.255
&& ip.getHostAddress().indexOf(":") == -1) {
return ip.getHostAddress();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
技术之家希望本篇文章对你有用!!
欢迎来到技术之家,
如需转载,烦请保留本文链接和出处:http://www.jszja.com/contents/14/1454.html
您的支持将是我们前进的动力!如对本篇文章有疑问或建议,请通过本站下方邮箱联系我们,让技术之家每天进步一点点!(●'◡'●)