有3次握手了,为啥还有4次挥手?#
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
作用#
确保数据能够完整传输
Redis分布式锁实现源码讲解#
图文讲解#
步骤#
- 分布式锁满足两个条件,一个是加有效时间的锁,一个是高性能解锁
- 采用redis命令setnx (set if not exist)、setex(set expire value)实现
- 解锁流程不能遗漏,否则导致任务执行一次就永不过期
- 将加锁代码和任务逻辑放到try catch代码块,解锁流程放到finally代码块
项目结构#
pom.xml#
<?xml version="1.0" encoding="UTF-8"?> 4.0.0 org.springframework.boot spring-boot-starter-parent 2.3.3.RELEASE com.cyb yb-mobile-redis 0.0.1-SNAPSHOT yb-mobile-redis Demo project for Spring Boot
application.properties#
spring.redis.database=0 spring.redis.host=192.168.199.142 spring.redis.port=6379 spring.redis.password=12345678 server.port=9001
RedisService.java#
package com.cyb.ybmobileredis.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Service; import java.io.Serializable; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @ClassName:RedisService * @Description:TODO * @Author:chenyb * @Date:2020/8/16 5:39 下午 * @Versiion:1.0 */ @Service public class RedisService { @Autowired private RedisTemplate redisTemplate; private static double size = Math.pow(2, 32); /** * 写入缓存 * * @param key * @param offset 位 8Bit=1Byte * @return */ public boolean setBit(String key, long offset, boolean isShow) { boolean result = false; try { ValueOperations
LockNxExJob.java#
package com.cyb.ybmobileredis.schedule; import com.cyb.ybmobileredis.service.RedisService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; /** * @ClassName:LockNxExJob * @Description:分布式获取锁和释放锁 * @Author:chenyb * @Date:2020/8/16 5:44 下午 * @Versiion:1.0 */ @Service public class LockNxExJob { private static final Logger logger = LoggerFactory.getLogger(LockNxExJob.class); @Autowired private RedisService redisService; @Autowired private RedisTemplate redisTemplate; private static String LOCK_PREFIX = "prefix_"; @Scheduled(fixedRate = 8000) public void lockJob() { String lock = LOCK_PREFIX + "LockNxExJob"; boolean nxRet=false; try{ //redistemplate setnx操作 nxRet = redisTemplate.opsForValue().setIfAbsent(lock,getHostIp()); Object lockValue = redisService.get(lock); System.out.println(lockValue); //获取锁失败 if(!nxRet){ String value = (String)redisService.get(lock); //打印当前占用锁的服务器IP logger.info(System.currentTimeMillis()+" get lock fail,lock belong to:{}",value); return; }else{ redisTemplate.opsForValue().set(lock,getHostIp(),3600); //获取锁成功 logger.info(System.currentTimeMillis()+" start lock lockNxExJob success"); Thread.sleep(4000); } }catch (Exception e){ logger.error("lock error",e); }finally { if (nxRet){ System.out.println("释放锁成功"); redisService.remove(lock); } } } /** * 获取本机内网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; } }
验证#
打个jar运行在Linux上,一个在本地运行,一个获取锁成功,一个获取锁失败
Redis分布式锁可能出现的问题#
上面我们已经使用代码实现了分布式锁的功能,同一时刻****只能一把锁获取成功。从上图可以看出,极端情况下,第一个Server获取锁成功后,服务或者Redis宕机了,会导致Redis锁无法释放的问题,其他的Server就一直获取锁失败。
模拟server获取锁宕机#
先把项目跑起来,获取锁之后,立马kill -9 进程id,杀掉当前进程,然后在运行项目,控制台就会一直提示,获取锁失败了。
解决方案(重点)#
- 一次性执行一条命令就不会出现该情况发生,采用Lua脚本
- Redis从2.6之后,支持setnx、setex连用
lua脚本#
- 在redource目录下新增一个后缀名为.lua结尾的文件
- 编写lua脚本
- 传入lua脚本的key和arg
- 调用redisTemplate.execute方法执行脚本
编写lua脚本
[](https://img2020.cnblog
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
s.com/blog/1504448/202008/1504448-20200819224859707-963451524.png)
local lockKey = KEYS[1] local lockValue = KEYS[2] -- setnx info local result_1 = redis.call('SETNX',lockKey,lockValue) if result_1 == true then local result_2 = redis.call('SETEX',lockKey,3600,lockValue) return result_1 else return result_1 end
封装调用lua脚本方法
@Autowired private RedisTemplate redisTemplate; private DefaultRedisScript lockScript; /** * 获取lua结果 * * @param key 键 * @param value 值 * @return */ public Boolean luaExpress(String key, String value) { lockScript = new DefaultRedisScript<>(); lockScript.setScriptSource( new ResourceScriptSource(new ClassPathResource("add.lua")) ); //设置返回值 lockScript.setResultType(Boolean.class); //封装参数 List keyList = new ArrayList<>(); keyList.add(key); keyList.add(value); Boolean result = (Boolean) redisTemplate.execute(lockScript, keyList); return result; }
改造之前的分布式锁方法
package com.cyb.ybmobileredis.schedule; import com.cyb.ybmobileredis.service.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.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scripting.support.ResourceScriptSource; import org.springframework.stereotype.Service; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; /** * @ClassName:LockNxExJob * @Description:分布式获取锁和释放锁 * @Author:chenyb * @Date:2020/8/16 5:44 下午 * @Versiion:1.0 */ @Service public class LockNxExJob { private static final Logger logger = LoggerFactory.getLogger(LockNxExJob.class); @Autowired private RedisService redisService; @Autowired private RedisTemplate redisTemplate; private static String LOCK_PREFIX = "prefix_"; private DefaultRedisScript lockScript; //一般分布式锁 // @Scheduled(fixedRate = 8000) // public void lockJob() { // String lock = LOCK_PREFIX + "LockNxExJob"; // boolean nxRet = false; // try { // //redistemplate setnx操作 // nxRet = redisTemplate.opsForValue().setIfAbsent(lock, getHostIp()); // Object lockValue = redisService.get(lock); // System.out.println(lockValue); // //获取锁失败 // if (!nxRet) { // String value = (String) redisService.get(lock); // //打印当前占用锁的服务器IP // logger.info(System.currentTimeMillis() + " get lock fail,lock belong to:{}", value); // return; // } else { // redisTemplate.opsForValue().set(lock, getHostIp(), 3600000); // // //获取锁成功 // logger.info(System.currentTimeMillis() + " start lock lockNxExJob success"); // Thread.sleep(4000); // } // } catch (Exception e) { // logger.error("lock error", e); // // } finally { // if (nxRet) { // System.out.println("释放锁成功"); // redisService.remove(lock); // } // } // } /** * lua脚本方式分布式锁 */ @Scheduled(fixedRate = 8000) public void luaLockJob() { String lock = LOCK_PREFIX + "LockNxExJob"; boolean nxRet = false; try { //redistemplate setnx操作 nxRet = luaExpress(lock,getHostIp()); Object lockValue = redisService.get(lock); System.out.println(lockValue); //获取锁失败 if (!nxRet) { String value = (String) redisService.get(lock); //打印当前占用锁的服务器IP logger.info(System.currentTimeMillis() + " lua get lock fail,lock belong to:{}", value); return; } else { redisTemplate.opsForValue().set(lock, getHostIp(), 3600000); //获取锁成功 logger.info(System.currentTimeMillis() + " lua start lock lockNxExJob success"); Thread.sleep(4000); } } catch (Exception e) { logger.error("lua lock error", e); } finally { if (nxRet) { System.out.println("lua 释放锁成功"); redisService.remove(lock); } } } /** * 获取lua结果 * * @param key 键 * @param value 值 * @return */ public Boolean luaExpress(String key, String value) { lockScript = new DefaultRedisScript<>(); lockScript.setScriptSource( new ResourceScriptSource(new ClassPathResource("add.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; } }
验证#
补充解决Redis中的key乱码问题#
只需要添加RedisConfig.java配置文件即可
package com.cyb.ybmobileredis.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * @ClassName:RedisConfig * @Description:Redis配置类 * @Author:chenyb * @Date:2020/8/16 11:48 下午 * @Versiion:1.0 */ @Configuration //当前类为配置类 public class RedisConfig { @Bean //redisTemplate注入到Spring容器 public RedisTemplate
RedisConnection实现分布式锁#
简介
RedisConnection实现分布式锁的方式,采用redisTemplate操作redisConnection实现setnx和setex两个命令连用
代码实现
package com.cyb.ybmobileredis.schedule; import com.cyb.ybmobileredis.service.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.Service; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; /** * @ClassName:LockNxExJob * @Description:分布式获取锁和释放锁 * @Author:chenyb * @Date:2020/8/16 5:44 下午 * @Versiion:1.0 */ @Service public class LockNxExJob { private static final Logger logger = LoggerFactory.getLogger(LockNxExJob.class); @Autowired private RedisService redisService; @Autowired private RedisTemplate redisTemplate; private static String LOCK_PREFIX = "prefix_"; private DefaultRedisScript lockScript; //一般分布式锁 // @Scheduled(fixedRate = 8000) // public void lockJob() { // String lock = LOCK_PREFIX + "LockNxExJob"; // boolean nxRet = false; // try { // //redistemplate setnx操作 // nxRet = redisTemplate.opsForValue().setIfAbsent(lock, getHostIp()); // Object lockValue = redisService.get(lock); // System.out.println(lockValue); // //获取锁失败 // if (!nxRet) { // String value = (String) redisService.get(lock); // //打印当前占用锁的服务器IP // logger.info(System.currentTimeMillis() + " get lock fail,lock belong to:{}", value); // return; // } else { // redisTemplate.opsForValue().set(lock, getHostIp(), 3600000); // // //获取锁成功 // logger.info(System.currentTimeMillis() + " start lock lockNxExJob success"); // Thread.sleep(4000); // } // } catch (Exception e) { // logger.error("lock error", e); // // } finally { // if (nxRet) { // System.out.println("释放锁成功"); // redisService.remove(lock); // } // } // } /** * lua脚本方式分布式锁 */ // @Scheduled(fixedRate = 8000) // public void luaLockJob() { // String lock = LOCK_PREFIX + "LockNxExJob"; // boolean nxRet = false; // try { // //redistemplate setnx操作 // //nxRet = luaExpress(lock,getHostIp()); // nxRet = setLock(lock,600); // Object lockValue = redisService.get(lock); // System.out.println(lockValue); // //获取锁失败 // if (!nxRet) { // String value = (String) redisService.get(lock); // //打印当前占用锁的服务器IP // logger.info(System.currentTimeMillis() + " lua get lock fail,lock belong to:{}", value); // return; // } else { // redisTemplate.opsForValue().set(lock, getHostIp(), 3600000); // // //获取锁成功 // logger.info(System.currentTimeMillis() + " lua start lock lockNxExJob success"); // Thread.sleep(4000); // } // } catch (Exception e) { // logger.error("lua lock error", e); // // } finally { // if (nxRet) { // System.out.println("lua 释放锁成功"); // redisService.remove(lock); // } // } // } /** * setnx和setex连用分布式锁 */ @Scheduled(fixedRate = 8000) public void setLockJob() { String lock = LOCK_PREFIX + "LockNxExJob"; boolean nxRet = false; try { //redistemplate setnx操作 //nxRet = luaExpress(lock,getHostIp()); nxRet = setLock(lock,getHostIp(),3); Object lockValue = redisService.get(lock); System.out.println(lockValue); //获取锁失败 if (!nxRet) { String value = (String) redisService.get(lock); //打印当前占用锁的服务器IP logger.info(System.currentTimeMillis() + " setnx and setex get lock fail,lock belong to:{}", value); return; } else { redisTemplate.opsForValue().set(lock, getHostIp(), 3600000); //获取锁成功 logger.info(System.currentTimeMillis() + " setnx and setex start lock lockNxExJob success"); Thread.sleep(4000); } } catch (Exception e) { logger.error(" setnx and setex lock error", e); } finally { if (nxRet) { System.out.println(" setnx and setex 释放锁成功"); redisService.remove(lock); } } } /** * setnx和setex连用 * @param key 键 * @param value 值 * @param expire 超时时间 * @return */ public boolean setLock(String key,String value,long expire){ try{ Boolean result=(boolean)redisTemplate.execute(new RedisCallback() { @Override public Boolean doInRedis(RedisConnection connection) throws DataAccessException { return connection.set(key.getBytes(),value.getBytes(),Expiration.seconds(expire),RedisStringCommands.SetOption.ifAbsent()); } }); return result; }catch (Exception e){ logger.error("set redis occured an exception",e); } return false; } /** * 获取lua结果 * * @param key 键 * @param value 值 * @return */ public Boolean luaExpress(String key, String value) { lockScript = new DefaultRedisScript<>(); lockScript.setScriptSource( new ResourceScriptSource(new ClassPathResource("add.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; } }
测试#
分布式锁优化细节(重点)#
上面几个案例,已经实现了分布式锁的功能,但是极端情况下,ServerA程序还没执行完,ServerB程序执行完,把锁释放掉了,就会造成A的锁释放掉了,这不是扯嘛,ServerA还没执行完,锁就被其他人释放了。解决方案:释放的时候,使用lua,通过get方法获取value,判断value是否等于本机ip,是自己的才能释放
package com.cyb.ybmobileredis.schedule; import com.cyb.ybmobileredis.service.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.Service; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; /** * @ClassName:LockNxExJob * @Description:分布式获取锁和释放锁 * @Author:chenyb * @Date:2020/8/16 5:44 下午 * @Versiion:1.0 */ @Service public class LockNxExJob { private static final Logger logger = LoggerFactory.getLogger(LockNxExJob.class); @Autowired private RedisService redisService; @Autowired private RedisTemplate redisTemplate; private static String LOCK_PREFIX = "prefix_"; private DefaultRedisScript lockScript; //一般分布式锁 // @Scheduled(fixedRate = 8000) // public void lockJob() { // String lock = LOCK_PREFIX + "LockNxExJob"; // boolean nxRet = false; // try { // //redistemplate setnx操作 // nxRet = redisTemplate.opsForValue().setIfAbsent(lock, getHostIp()); // Object lockValue = redisService.get(lock); // System.out.println(lockValue); // //获取锁失败 // if (!nxRet) { // String value = (String) redisService.get(lock); // //打印当前占用锁的服务器IP // logger.info(System.currentTimeMillis() + " get lock fail,lock belong to:{}", value); // return; // } else { // redisTemplate.opsForValue().set(lock, getHostIp(), 3600000); // // //获取锁成功 // logger.info(System.currentTimeMillis() + " start lock lockNxExJob success"); // Thread.sleep(4000); // } // } catch (Exception e) { // logger.error("lock error", e); // // } finally { // if (nxRet) { // System.out.println("释放锁成功"); // redisService.remove(lock); // } // } // } /** * lua脚本方式分布式锁 */ // @Scheduled(fixedRate = 8000) // public void luaLockJob() { // String lock = LOCK_PREFIX + "LockNxExJob"; // boolean nxRet = false; // try { // //redistemplate setnx操作 // //nxRet = luaExpress(lock,getHostIp()); // nxRet = setLock(lock,600); // Object lockValue = redisService.get(lock); // System.out.println(lockValue); // //获取锁失败 // if (!nxRet) { // String value = (String) redisService.get(lock); // //打印当前占用锁的服务器IP // logger.info(System.currentTimeMillis() + " lua get lock fail,lock belong to:{}", value); // return; // } else { // redisTemplate.opsForValue().set(lock, getHostIp(), 3600000); // // //获取锁成功 // logger.info(System.currentTimeMillis() + " lua start lock lockNxExJob success"); // Thread.sleep(4000); // } // } catch (Exception e) { // logger.error("lua lock error", e); // // } finally { // if (nxRet) { // System.out.println("lua 释放锁成功"); // redisService.remove(lock); // } // } // } /** * setnx和setex连用分布式锁 */ @Scheduled(fixedRate = 8000) public void setLockJob() { String lock = LOCK_PREFIX + "LockNxExJob"; boolean nxRet = false; try { //redistemplate setnx操作 //nxRet = luaExpress(lock,getHostIp()); System.out.println("hostIp1="+getHostIp()); nxRet = setLock(lock, getHostIp(), 30); Object lockValue = redisService.get(lock); System.out.println(lockValue); //获取锁失败 if (!nxRet) { String value = (String) redisService.get(lock); //打印当前占用锁的服务器IP logger.info(System.currentTimeMillis() + " setnx and setex get lock fail,lock belong to:{}", value); return; } else { //获取锁成功 logger.info(System.currentTimeMillis() + " setnx and setex start lock lockNxExJob success"); Thread.sleep(4000); } } catch (Exception e) { logger.error(" setnx and setex lock error", e); } finally { if (nxRet) { System.out.println(" setnx and setex 释放锁成功"); //redisService.remove(lock); //使用lua脚本释放锁 System.out.println("hostIp2="+getHostIp()); Boolean result = releaseLock(lock, getHostIp()); System.out.println("状态:"+result); } } } /** * 释放锁操作 * * @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; } /** * setnx和setex连用 * * @param key 键 * @param value 值 * @param expire 超时时间 * @return */ public boolean setLock(String key, String value, long expire) { try { Boolean result = (boolean) redisTemplate.execute(new RedisCallback() { @Override public Boolean doInRedis(RedisConnection connection) throws DataAccessException { return connection.set(key.getBytes(), value.getBytes(), Expiration.seconds(expire), RedisStringCommands.SetOption.ifAbsent()); } }); return result; } catch (Exception e) { logger.error("set redis occured an exception", e); } return false; } /** * 获取lua结果 * * @param key 键 * @param value 值 (), value.getBytes(), Expiration.seconds(expire), RedisStringCommands.SetOption.ifAbsent()); } }); return result; } catch (Exception e) { logger.error("set redis occured an exception", e); } return false; } /** * 获取lua结果 * * @param key 键 * @param value 值