解决Redis中Unexpected token (START_OBJECT)错误
在使用Redis存储复杂对象(如Java对象)时,我们可能会遇到这样的错误信息:
Unexpected token (START_OBJECT), expected VALUE_STRING: need String, Number or Boolean value that contains type id (for subtype of java.lang.Object)
这个错误通常发生在尝试将一个复杂对象直接存储到Redis中,而没有正确地序列化。
问题描述
当我们尝试从Redis读取一个预期为字符串、数字或布尔值的值时,如果实际存储的是一个未正确序列化的复杂对象,就会遇到上述错误。
排查过程
1. 配置Redisson客户端
根据网上的教程,我尝试配置Redisson客户端以支持JSON序列化,特别是处理LocalDateTime
异常:
@Bean
public RedissonClient redisson() throws IOException {
Config config = Config.fromYAML(configFile.getInputStream());
// 修复引入Redisson存储json序列化配置时提示LocalDateTime异常
JsonJacksonCodec jacksonCodec = new JsonJacksonCodec();
jacksonCodec.getObjectMapper()
// 补充支持jsr310
.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
config.setCodec(jacksonCodec);
// 修复引入Redisson存储json序列化配置时提示LocalDateTime异常
return Redisson.create(config);
}
然而,这个配置并没有解决问题。
检查Redis中的数据
进一步排查时,我去Redis中查看了存储的数据,发现存储的是数组,而不是预期的对象类型。这可能是导致异常的原因。
指定存储的对象类型
怀疑是因为Redis中存储的数据类型不正确导致异常,我在设置Redis时再次指定了存储的对象类型。这次,我确保了存储的数据是正确序列化的JSON字符串,而不是数组或其他类型。
方案一
确保在存储到Redis之前,将复杂对象正确序列化为JSON字符串。这可以通过使用Jackson或Gson等库来实现。以下是使用Jackson和Gson序列化和反序列化的示例代码:
方案二
使用Kryo5Codec
另一方面,如果使用Kryo5Codec,则不需要指定返回的类型,因为Kryo5Codec可以自动处理对象的序列化和反序列化。
# redisson.yaml
# 编码。默认值: org.redisson.codec.JsonJacksonCodec
codec: !<org.redisson.codec.Kryo5Codec> {}
# 线程池数量。默认值: 当前处理核数量 * 2
threads: 16
# Netty线程池数量。默认值: 当前处理核数量 * 2
nettyThreads: 32
# 传输模式。默认值: NIO
transportMode: "NIO"
# 监控锁的看门狗超时,单位:毫秒。默认值: 30000
lockWatchdogTimeout: 30000
# 是否保持订阅发布顺序。默认值: true
keepPubSubOrder: true
# Redisson 单实例配置
singleServerConfig:
# 节点地址。格式:redis://host:port
address: "redis://xxx.xxx.xxx.xxx:xxxx"
# 密码。默认值: null
password: null
# 数据库编号。默认值: 0
database: 0
# 客户端名称(在Redis节点里显示的客户端名称)。默认值: null
clientName: null
# 连接超时,单位:毫秒。默认值: 10000
connectTimeout: 10000
# 命令等待超时,单位:毫秒。默认值: 3000
timeout: 3000
# 命令失败重试次数。默认值: 3
retryAttempts: 3
# 命令重试发送时间间隔,单位:毫秒。默认值: 1500
retryInterval: 1500
# 最小空闲连接数。默认值: 32
connectionMinimumIdleSize: 24
# 连接池大小。默认值: 64
connectionPoolSize: 64
# 单个连接最大订阅数量。默认值: 5
subscriptionsPerConnection: 5
# 发布和订阅连接的最小空闲连接数。默认值: 1
subscriptionConnectionMinimumIdleSize: 1
# 发布和订阅连接池大小。默认值: 50
subscriptionConnectionPoolSize: 50
# DNS监测时间间隔,单位:毫秒。默认值: 5000
dnsMonitoringInterval: 5000
# 连接空闲超时,单位:毫秒。默认值: 10000
idleConnectionTimeout: 10000
结论
通过确保在存储到Redis之前将复杂对象正确序列化为JSON字符串,并在读取时正确反序列化,我们可以避免遇到Unexpected token (START_OBJECT)错误。这要求我们在存储和读取时使用相同的序列化/反序列化方法,并确保数据类型一致。