【Java】RedisTemplate 执行lua脚本出错

使用lua脚本释放锁时,lua 脚本中返回值为 int 时出现异常,但是将返回值修改为 string 时却能正常使用

错误信息如下:

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)

at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)

at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)

at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)

at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:257)

at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.convertLettuceAccessException(LettuceScriptingCommands.java:236)

at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:195)

at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1240)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61)

at com.sun.proxy.$Proxy59.evalSha(Unknown Source)

at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77)

at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:68)

at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)

at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)

at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:171)

at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58)

at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52)

at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:346)

at spring.redis.handler.RedisHandler.luaReleaseLock(RedisHandler.java:49)

at spring.redis.handler.RedisHandlerTest.luaReleaseLock(RedisHandlerTest.java:36)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)

at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)

at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)

at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)

at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)

at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)

at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)

at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)

at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)

at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)

at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)

at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)

at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)

at org.junit.runners.ParentRunner.run(ParentRunner.java:363)

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)

at org.junit.runner.JUnitCore.run(JUnitCore.java:137)

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)

at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)

at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)

at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

Caused by: io.lettuce.core.RedisException: java.lang.IllegalStateException

at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:125)

at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:62)

at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)

at com.sun.proxy.$Proxy57.evalsha(Unknown Source)

at org.springframework.data.redis.connection.lettuce.LettuceScriptingCommands.evalSha(LettuceScriptingCommands.java:193)

... 47 more

Caused by: java.lang.IllegalStateException

at io.lettuce.core.output.CommandOutput.set(CommandOutput.java:75)

at io.lettuce.core.protocol.RedisStateMachine.safeSet(RedisStateMachine.java:357)

at io.lettuce.core.protocol.RedisStateMachine.decode(RedisStateMachine.java:138)

at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:629)

at io.lettuce.core.protocol.CommandHandler.decode0(CommandHandler.java:604)

at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:599)

at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:547)

at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:516)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)

at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)

at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)

at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)

at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)

at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)

at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1414)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)

at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:945)

at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:146)

at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:645)

at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:580)

at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:497)

at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:459)

at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886)

at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)

at java.lang.Thread.run(Thread.java:745)

在lua脚本中返回 string 类型时缺没问题

详细代码如下:

@Component

public class RedisHandler {

private final Logger logger = LoggerFactory.getLogger(RedisHandler.class);

@Autowired

RedisTemplate redisTemplate;

public Object luaReleaseLock(String key, String value) {

String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end ";

DefaultRedisScript<Integer> redisScript = new DefaultRedisScript<>();

redisScript.setScriptText(script);

redisScript.setResultType(Integer.class);

redisTemplate.execute(redisScript, Collections.singletonList(key), value);

return null;

}

}

RedisTemplate 配置

@Bean

public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {

RedisTemplate redisTemplate = new RedisTemplate();

redisTemplate.setConnectionFactory(redisConnectionFactory);

GenericJackson2JsonRedisSerializer redisSerializer = new GenericJackson2JsonRedisSerializer();

redisTemplate.setDefaultSerializer(redisSerializer);

return redisTemplate;

}

POM

 <properties>

<jackson.version>2.9.4</jackson.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

</dependency>

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>${jackson.version}</version>

</dependency>

</dependencies>

而外还有一个问题是 redisTemplate.execute 返回定义的为泛型,为何返回值却是 object

@Override

public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {

return scriptExecutor.execute(script, keys, args);

}

【Java】RedisTemplate 执行lua脚本出错

回答

我也遇到这个错误,搞不清楚。换个实现方式就行了
`/**

 * 释放锁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

* @param uuid

* @return

*/

private boolean releaseLock(String key, String uuid) {

log.info("release lock:{key:{},uuid:{}}", key, uuid);

return redisTemplate.execute(

(RedisConnection connection) -> connection.eval(

RELEASE_LOCK_LUA_SCRIPT.getBytes(),

ReturnType.INTEGER,

1,

key.getBytes(),

uuid.getBytes())

).equals(RELEASE_LOCK_SUCCESS_RESULT);

}`

我也遇到了这个问题,"无辣不欢"的答案是可以实现的,只是实现方式有点底层。 实际上不用那么麻烦,使用DefaultRedisScript就可以了,直接指定ReturnType

/**

*

* 释放锁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

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);

}

我也遇到了,最开始redis-cli执行完全没有问题, 然后就只能取跟踪代码了,最后看到这 org.springframework.data.redis.connection.ReturnType , 这里面redis没有对应的Integer ,只有使用Long

这里的类型对应redis的回复协议,可以了解下下,哈哈

贴上一个代码

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;

}

以上是 【Java】RedisTemplate 执行lua脚本出错 的全部内容, 来源链接: utcz.com/a/86510.html

回到顶部