Spring Boot Redis increment() 后获取结果失败

yufei       3 年, 11 月 前       2030

刚刚在练习 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 数据来序列化

因此:

  1. 如果我们调用 set 或者 get 等方法设置获取非字符串类型,比如 Long 时的结果就会类似于 \xac\xed\x00\x05t\x00\nview:count

  2. 但是为什么 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 的 incrdecr 返回的永远是 Long 类型,但是 get 返回的永远是 String 类型

所以 redisTemplate.setValueSerializer(new StringRedisSerializer()); 不能少

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.