前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >RedisTemplate执行lua脚本抛出异常IllegalStateException

RedisTemplate执行lua脚本抛出异常IllegalStateException

作者头像
十毛
发布于 2019-10-24 03:00:28
发布于 2019-10-24 03:00:28
6.4K01
代码可运行
举报
运行总次数:1
代码可运行

基于Redis的分布式锁的释放过程,为了防止释放错误,需要使用lua脚本实现原子释放,但是RedisTemplate在执行lua脚本时会抛出异常IllegalStateException

问题描述

  • 分布式锁的释放
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 *
 * 释放锁lua脚本
*/
private static final String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
 * 释放锁成功返回值
 */
private static final Long RELEASE_LOCK_SUCCESS_RESULT = 1L;

/**
 * 释放锁
 *
 * @param key      锁ID
 * @param clientId 客户端ID
 * @return 是否成功
 */
private boolean releaseLock(String key, String clientId) {
    log.info("release lock:{key:{},clientId:{}}", key, clientId);
    Long result = stringRedisTemplate.execute(RedisScript.of(RELEASE_LOCK_LUA_SCRIPT), Collections.singletonList(key), clientId);
    return Objects.equals(result, RELEASE_LOCK_SUCCESS_RESULT);
}
  • 异常信息
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java.lang.IllegalStateException: Failed to execute ApplicationRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:778) [spring-boot-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:765) [spring-boot-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) [spring-boot-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at com.tenmao.redislock.RedisLockApplication.main(RedisLockApplication.java:40) [classes/:na]
Caused by: org.springframework.data.redis.RedisSystemException: Redis exception; nested exception is io.lettuce.core.RedisException: java.lang.IllegalStateException
    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:74) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:269) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.convertLettuceAccessException(LettuceScriptingCommands.java:236) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:195) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1440) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.connection.DefaultStringRedisConnection.evalSha(DefaultStringRedisConnection.java:1750) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_144]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_144]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_144]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_144]
    at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at com.sun.proxy.$Proxy51.evalSha(Unknown Source) ~[na:na]
    at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:68) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:175) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:350) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    at com.tenmao.redislock.RedisLockApplication.run(RedisLockApplication.java:47) [classes/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:775) [spring-boot-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    ... 5 common frames omitted
Caused by: io.lettuce.core.RedisException: java.lang.IllegalStateException
    at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:129) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at com.sun.proxy.$Proxy48.evalsha(Unknown Source) ~[na:na]
    at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:193) ~[spring-data-redis-2.2.0.RELEASE.jar:2.2.0.RELEASE]
    ... 23 common frames omitted
Caused by: java.lang.IllegalStateException: null
    at io.lettuce.core.output.CommandOutput.set(CommandOutput.java:85) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.protocol.RedisStateMachine.safeSet(RedisStateMachine.java:357) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.protocol.RedisStateMachine.decode(RedisStateMachine.java:138) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:714) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.protocol.CommandHandler.decode0(CommandHandler.java:678) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:673) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:594) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:563) ~[lettuce-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:352) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1422) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:374) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:360) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:931) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:700) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:635) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:552) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:514) ~[netty-transport-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044) ~[netty-common-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.42.Final.jar:4.1.42.Final]
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.42.Final.jar:4.1.42.Final]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_144]

原因分析

因为没有指定ReturnType,所以默认使用ReturnType.STATUS,返回值就是io.lettuce.core.output.StatusOutput,这个类并没有重写CommandOutput中的方法。所以抛出异常IllegalStateException

  • CommandOutput中的set(long integer)方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Set the command output to a 64-bit signed integer. Concrete {@link CommandOutput} implementations must override this
 * method unless they only receive a byte array value.
 *
 * @param integer The command output.
 */
public void set(long integer) {
    throw new IllegalStateException();
}

解决方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 *
 * 释放锁lua脚本
*/
private static final String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
 * 释放锁成功返回值
 */
private static final Long RELEASE_LOCK_SUCCESS_RESULT = 1L;

/**
 * 释放锁
 *
 * @param key      锁ID
 * @param clientId 客户端ID
 * @return 是否成功
 */
private boolean releaseLock(String key, String clientId) {
    log.info("release lock:{key:{},clientId:{}}", key, clientId);
    //指定ReturnType为Long.class,注意这里不能使用Integer.class,因为ReturnType不支持。只支持List.class, Boolean.class和Long.class
    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT, Long.class);
    Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), clientId);
    return Objects.equals(result, RELEASE_LOCK_SUCCESS_RESULT);
}
  • ReturnType与Java类型的对应关系
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static ReturnType fromJavaType(@Nullable Class<?> javaType) {

    if (javaType == null) {
        return ReturnType.STATUS;
    }
    if (javaType.isAssignableFrom(List.class)) {
        return ReturnType.MULTI;
    }
    if (javaType.isAssignableFrom(Boolean.class)) {
        return ReturnType.BOOLEAN;
    }
    if (javaType.isAssignableFrom(Long.class)) {
        return ReturnType.INTEGER;
    }
    return ReturnType.VALUE;
}

参考

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.10.22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Cloud Gateway 雪崩了,我 TM 人傻了
5.重启后的网关进程,立刻请求数量激增,每个实例峰值 2000 qps,闲时每个实例 500 qps,忙时由于有扩容也能保持每个实例在 1000 qps 以内,然后健康检查接口就很长时间没有响应,导致实例不断重启
干货满满张哈希
2021/12/30
1.6K0
Spring Cloud Gateway 雪崩了,我 TM 人傻了
"Unable to start embedded Tomcat" 问题
网上说了很多解决办法, 有的是手动配置配置一个外置的Tomcat, 有的说是jar包冲突. 仔细想想应该都没有把握核心, Springboot已经集成了内置的Tomcat, 所以没有必要再配置一个外置的Tomcat. 错误日志有一句信息很重要:
芥末鱿鱼
2022/05/05
1.6K0
springboot整合redis一直报远程主机强迫关闭了一个现有的连接
需要修改一下2处,好像timeout 默认就是0,tcp-keepalive默认就是300,那为什么还回报错呢?可能是启动的时候没有指定配置文件
九转成圣
2024/05/08
5510
spring boot启动时redis报错: DENIED Redis is running in protected mode, no bind address was specified。
临时解决方法,去除redis的安全模式。 打开reids配置文件,修改安全模式为:no
leehao
2025/02/11
900
聊聊lettuce的指标监控
lettuce-core-5.0.4.RELEASE-sources.jar!/io/lettuce/core/event/metrics/DefaultCommandLatencyEventPublisher.java
code4it
2018/09/17
2.3K0
Spring Data Redis 最佳实践!
使用该注解的方法当缓存存在时,会从缓存中获取数据而不执行方法,当缓存不存在时,会执行方法并把返回结果存入缓存中。一般使用在查询方法上,可以设置如下属性:
macrozheng
2020/03/26
1.6K0
SpringCloudGateway笔记(11)-异常拦截
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
yingzi_code
2019/08/30
1.1K0
无快不破,在本地 docker 运行 IDEA 里面的项目?
前面已经搭建好了本地基于springboot,redis,mybatis的项目,其中redis,mybatis都是在docker中运行的,但是整个项目还是在IDEA上运行的,不如折腾一下,让项目在docker上跑起来。
秦怀杂货店
2022/02/17
1.2K0
无快不破,在本地 docker 运行 IDEA 里面的项目?
一起来学SpringBoot | 第九篇:整合Lettuce Redis
SpringBoot 除了支持常见的ORM框架外,更是对常用的中间件提供了非常好封装,随着 SpringBoot2.x的到来,支持的组件越来越丰富,也越来越成熟,其中对 Redis的支持不仅仅是丰富了它的API,更是替换掉底层 Jedis的依赖,取而代之换成了 Lettuce(生菜)
battcn
2018/08/03
1.8K0
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立P
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
卓伊凡
2025/02/10
660
SpringCloud Gateway 测试问题解决
本文遇到的问题都是在测试环境真正遇到的问题,不一定试用于所有人,仅做一次记录,便于遇到同样问题的干掉这些问题。
猿天地
2019/05/07
2.6K0
SpringCloud Gateway 测试问题解决
webclient的超时时间配置
SO_TIMEOUT只适用于OIO,对于NIO不适用;使用ReadTimeoutHandler 或者block(Duration)都关闭client端,非reactive的服务端无法感知Terminated,reactive的服务端可以感知到Terminated
code4it
2018/09/17
8.8K0
ES6.8.2 kibana的monitoring访问报错We couldn't activate monitoring处理过程
这个错误是由于Elasticsearch 6.x版本开始,一个索引只能有一个类型,但是在你的监控索引.monitoring-kibana-6-2023.09.28中,却试图创建两个类型_doc和doc,这是不被允许的。
zjiekou
2023/09/28
1.2K0
ES6.8.2 kibana的monitoring访问报错We couldn't activate monitoring处理过程
spring redis java.lang.IllegalStateException: Cannot load configuration class: redis.RedisTemplet报错
报错日志 java.lang.IllegalStateException: Cannot load configuration class: redis.RedisTemplet at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:414) ~[spring-context-5
MickyInvQ
2020/09/27
2.1K0
客户端启动报错java.lang.IllegalArgumentException: no server available的解决方案 SpringCloud中 Nacos做注册中心
我的错误就在这里,百度了很久也修改了很多配置 才发现是自己写的有问题; 大家在配置nacos的配置中心的时候 注意写法(很重要)
默 语
2024/11/20
1890
客户端启动报错java.lang.IllegalArgumentException: no server available的解决方案 SpringCloud中 Nacos做注册中心
惊人!Spring5 AOP 默认使用 CGLIB ?从现象到源码的深度分析
Spring5 AOP 默认使用CGLIB了?我第一次听到这个说法是在一个微信群里:
烟雨平生
2023/03/07
4970
惊人!Spring5 AOP 默认使用 CGLIB ?从现象到源码的深度分析
SpringCloudGateway笔记(5)-netty空指针问题
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
yingzi_code
2019/08/30
1.5K0
SpringBoot系列教程之Redis集群环境配置
之前介绍的几篇redis的博文都是基于单机的redis基础上进行演示说明的,然而在实际的生产环境中,使用redis集群的可能性应该是大于单机版的redis的,那么集群的redis如何操作呢?它的配置和单机的有什么区别,又有什么需要注意的呢?
一灰灰blog
2019/09/29
4K0
SpringBoot系列教程之Redis集群环境配置
Spring Cloud 2.x系列之spring cloud如何使用spring-test进行单元测试
上篇和大家学习了spring cloud 如何整合reids,在测试时借用了web形式的restful接口进行的。那还有没有别的方式可以对spring boot和spring cloud编写的代码进行单元测试呢?答案:肯定是有的。这篇讲解一下如何使用spring-boot-starter-test进行单元测试
BUG弄潮儿
2022/06/30
1.2K0
Spring Cloud 2.x系列之spring cloud如何使用spring-test进行单元测试
lettuce和hbase中对netty的使用你都了解吗?
在使用lettuce作为redis连接池时,在上一节中我们知道,lettuce中维护连接有两种使用连接池的方式,目前一种已经废弃,另一种大家正在使用的版本是apache commons pool。咱们来回顾下。
山行AI
2020/07/24
2.2K0
lettuce和hbase中对netty的使用你都了解吗?
推荐阅读
相关推荐
Spring Cloud Gateway 雪崩了,我 TM 人傻了
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验