spring redis运行脚本:如何传递到期时间值

Posted

技术标签:

【中文标题】spring redis运行脚本:如何传递到期时间值【英文标题】:spring redis running script : how to pass expiry time value 【发布时间】:2017-03-24 10:43:50 【问题描述】:

我用的是lua脚本:

local lock = redis.call('get', KEYS[1])
if not lock then    
    return redis.call('SETEX', KEYS[1], ARGV[1] ,ARGV[2] );
end
return false

从我用脚本调用redis的spring boot应用程序

DefaultRedisScript<Boolean> redisScript = new
DefaultRedisScript<Boolean>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("checkandset2.lua")));
redisScript.setResultType(Boolean.class);
System.out.println(redisTemplate.execute(redisScript , Collections.singletonList("value123"),"10" ,"key123"));

我总是遇到异常:

java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:32)
at org.springframework.data.redis.core.script.DefaultScriptExecutor.keysAndArgs(DefaultScriptExecutor.java:116)
at org.springframework.data.redis.core.script.DefaultScriptExecutor$1.doInRedis(DefaultScriptExecutor.java:63)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:202)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:164)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)
at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:60)
at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:54)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:298)
at com.masary.ledger.ResisScriptTestClass.msisdnJustRechargedException(ResisScriptTestClass.java:34)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

当我使用时

System.out.println(redisTemplate.execute(redisScript , Collections.singletonList("value123"),new Long(10) ,"key123"));

我得到异常

     org.springframework.data.redis.RedisSystemException: Unknown redis exception; nested exception is java.lang.ClassCastException: [B cannot be cast to java.lang.Long
        at org.springframework.data.redis.FallbackExceptionTranslationStrategy.getFallback(FallbackExceptionTranslationStrategy.java:48)
        at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:38)
        at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:212)
        at org.springframework.data.redis.connection.jedis.JedisConnection.evalSha(JedisConnection.java:3173)
        at org.springframework.data.redis.connection.jedis.JedisConnection.evalSha(JedisConnection.java:3158)
        at org.springframework.data.redis.connection.DefaultStringRedisConnection.evalSha(DefaultStringRedisConnection.java:1374)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:57)
        at com.sun.proxy.$Proxy182.evalSha(Unknown Source)
        at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:81)
        at org.springframework.data.redis.core.script.DefaultScriptExecutor$1.doInRedis(DefaultScriptExecutor.java:71)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:202)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:164)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)
        at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:60)
        at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:54)
        at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:298)
        at com.masary.ledger.ResisScriptTestClass.msisdnJustRechargedException(ResisScriptTestClass.java:34)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
.
.
    Caused by: java.lang.ClassCastException: [B cannot be cast to java.lang.Long
        at org.springframework.data.redis.connection.jedis.JedisScriptReturnConverter.convert(JedisScriptReturnConverter.java:53)
        at org.springframework.data.redis.connection.jedis.JedisConnection.evalSha(JedisConnection.java:3171)
        ... 46 more

任何建议如何将到期时间值传递给 lua 脚本?

【问题讨论】:

【参考方案1】:

该线程可能很旧,但可能对可能偶然发现此问题的其他人有所帮助。

对我来说,原因是使用JdkSerializationRedisSerializer作为值序列化器,比如:redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()),这也是默认的。

要解决这个问题,请参阅下面的修复:

private static void redisEvalForExpire(String scriptText, List<String> keys, Object[] argv) 
  // since spring boot serializes data to - "\xac\xed\x00\x05t\x00\x0" it was not being recognized by redis as integer
  redisTemplate.execute(new DefaultRedisScript<>(scriptText), new StringRedisSerializer(), new StringRedisSerializer(), keys, argv);

这表示任何值都使用StringRedisSerializer,所以当我们这样做时 - set a 2,使用JdkSerializationRedisSerializer,2 序列化为:

"\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x00\x02"

但是当我们说使用StringRedisSerializer 时,2 发送为"2",Redis 可以轻松识别。

Official Documentation

【讨论】:

【参考方案2】:

我自己也是新手,遇到了类似的问题。尝试发送字符串"10",而不是Long。为我工作。

【讨论】:

以上是关于spring redis运行脚本:如何传递到期时间值的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot - 如何获取正在运行的端口和IP地址[重复]

redis 能不能给列表或者合集里边的元素设置到期时间

java spring boot / spring security(HttpSecurity)中的会话到期时如何自动注销

如何在spring + java中添加(覆盖)oAuth2访问令牌的到期时间

如何让这个 init.d 脚本在服务器重新启动时启动?

如何将值从子脚本传递给同时运行的父脚本?