redis并发锁 Java_JAVA高并发之-RedisConnection实现分布式锁

本文介绍了如何使用 RedisConnection 和 redisTemplate 在 Java 中实现分布式锁,通过 setnx 和 setex 命令结合来确保锁的安全性。文章讨论了Spring Data Redis的集成服务,如Jedis,并展示了因版本升级导致的类转换错误问题,同时提供了源码改造的解决方案。
摘要由CSDN通过智能技术生成

*简介: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

您的支持将是我们前进的动力!如对本篇文章有疑问或建议,请通过本站下方邮箱联系我们,让技术之家每天进步一点点!(●'◡'●)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值