上周新上线一个功能,灰度验证通过,一上线就大量500,吓得要死,最后发现结果是redis反序列化失败造成的,由于线上是滚动发布,prd1刚发布,prd2.prd3就疯狂报错,因为新的代码里添加的字段,但是老的代码还没上,当读到新的缓存的时候就发现没有字段,反序列化失败,
临时解决方案,将缓存的名称改掉,新的代码走新缓存,老的走老缓存,上线后问题解决。
后来在复盘时,发现是redis设置的的序列化未生效,接下来看下报错:
报错:Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "feeMode" (, not marked as ignorable (20 known properties: "isMovie", "associatedSeason", "viewCount", "author", "area", "commentCount", "score", "favoriteCount", "duration", "cover", "title", "type", "id", "favorite", "year", "category", "upInfo", "status", "likeCount", "brief"])
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 设置了但是无效
尝试将其去掉,发现别的缓存序列化会有问题,证明只有我这块有问题。
周五花了一天的时间,试验出这种方式才可以解决,但是没有得到权威认证,目前自己测试已经没有问题了
由于redis可以设置默认的过期时间和序列化协议,如图:
RedisCacheManager.builder(factory)
//默认有效时间(30分钟)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(30 *60))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)))
.withInitialCacheConfigurations(cacheConfigMap)
.build();
但是有些缓存我们不想使用默认值就需要自己指定:
Map cacheConfigMap =new LinkedHashMap();
cacheConfigMap.put(CacheNames.***,RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10 *60)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
加粗的地方有问题,这里应当使用我们自己定义的GenericJackson2JsonRedisSerializer
public RedisConfig() {
this.deserializer = deserializer();
}
private GenericJackson2JsonRedisSerializer deserializer() {
ObjectMapper mapper =new ObjectMapper();
mapper.registerModule((new SimpleModule()).addSerializer(new NullValueSerializer(null)));
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
return new GenericJackson2JsonRedisSerializer(mapper);
}
加粗的地方就是忽略没有的字段
修改为:
cacheConfigMap.put(CacheNames.***,RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10 *60)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(deserializer)));
再添加字段,老代码走到新缓存也不会报错
总结下来:其实就是在设置序列化协议的时候没有使用规定的方式,而是自己new了一个新的,这样以前设置的忽略字段就没有生效,
所以在设置序列化协议的时候,应该使用以前人用过的,而不是自己随意改造。