刚刚在练习 Spring Boot 的定时计划任务,发现使用 increment()
之后调用 get()
失败。
源代码如下
package my.demo.component; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class SchedulerTaskOne { @Autowired private RedisTemplate redisTemplate; @Scheduled(cron="*/30 * * * * ?") private void incrCount() { ValueOperations<String, Long> op = redisTemplate.opsForValue(); Long count = op.increment("view:count"); System.out.println("view:count = " + count.toString()); } @Scheduled(cron="*/5 * * * * ?") private void printCount() { ValueOperations<String,Long> op = redisTemplate.opsForValue(); Long count = op.get("view:count"); System.out.println("print:view:count = " + count.toString()); } }
报错结果如下
2020-05-05 15:57:35.004 ERROR 25741 --- [pool-1-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.EOFException at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:84) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.data.redis.core.AbstractOperations.deserializeValue(AbstractOperations.java:335) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:61) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.data.redis.core.DefaultValueOperations.get(DefaultValueOperations.java:53) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] at my.demo.component.SchedulerTaskOne.printCount(SchedulerTaskOne.java:27) ~[main/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:567) ~[na:na] at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93) ~[spring-context-5.2.5.RELEASE.jar:5.2.5.RELEASE] at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na] at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na] at java.base/java.lang.Thread.run(Thread.java:830) ~[na:na] Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.EOFException at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:78) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:36) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:82) ~[spring-data-redis-2.2.6.RELEASE.jar:2.2.6.RELEASE] ... 20 common frames omitted Caused by: java.io.EOFException: null at java.base/java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2818) ~[na:na] at java.base/java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:3313) ~[na:na] at java.base/java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:908) ~[na:na] at java.base/java.io.ObjectInputStream.<init>(ObjectInputStream.java:350) ~[na:na] at org.springframework.core.ConfigurableObjectInputStream.<init>(ConfigurableObjectInputStream.java:65) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.core.ConfigurableObjectInputStream.<init>(ConfigurableObjectInputStream.java:51) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.core.serializer.DefaultDeserializer.deserialize(DefaultDeserializer.java:70) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE] at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:73) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE] ... 22 common frames omitted
而查看 Redis 上数据的键如下
127.0.0.1:6379> keys * 1) "\xac\xed\x00\x05t\x00\nview:count"
发现 view:count
前面竟然莫名其妙的加了一些特殊的字符串。
罪魁祸首找到了,可是为什么会这样子呢?
出现这个的原因是
默认情况下
RedisTempate
使用JdkSerializationRedisSerializer
数据来序列化
因此:
-
如果我们调用
set
或者get
等方法设置获取非字符串类型,比如Long
时的结果就会类似于\xac\xed\x00\x05t\x00\nview:count
-
但是为什么
increment()
没有报错? 那是应为这个命令总是返回Long
类型的数据。因此RedisTemplate
也就是不会调用反序列化操作了。
解决方法也很简单,就是设置 RedisTemplate
的反序列化类。
redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer());
完整代码如下
package my.demo.component; import com.fasterxml.jackson.databind.ser.std.StringSerializer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class SchedulerTaskOne { @Autowired private RedisTemplate redisTemplate; @Scheduled(cron="*/3 * * * * ?") private void incrCount() { ValueOperations<String, Long> op = redisTemplate.opsForValue(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); Long count = op.increment("view:count"); System.out.println("view:count = " + count.toString()); } @Scheduled(cron="*/5 * * * * ?") private void printCount() { ValueOperations<String,String> op = redisTemplate.opsForValue(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); String count = op.get("view:count"); System.out.println("print:view:count = " + count); } }
其实,这个还解决了另一个问题
Redis 的
incr
和decr
返回的永远是Long
类型,但是get
返回的永远是String
类型所以
redisTemplate.setValueSerializer(new StringRedisSerializer());
不能少
目前尚无回复