现象
使用spring boot的时候,缓存是常用的服务之一,放在缓存里的数据经常是个pojo,Java类放入缓存默认是通过序列化实现存储的。有时候升级改代码会增删一些属性,如果部署前忘记把相应的缓存先清除一下,就会遇到反序列化失败的异常了,异常信息一般如下:
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.InvalidClassException: com.package-of-pojo.Xxxx; local class incompatible: stream classdesc serialVersionUID = -2364286648166609117, local class serialVersionUID = -8974455668551700477
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:84)
at org.springframework.data.redis.serializer.DefaultRedisElementReader.read(DefaultRedisElementReader.java:48)
at org.springframework.data.redis.serializer.RedisSerializationContext$SerializationPair.read(RedisSerializationContext.java:272)
at org.springframework.data.redis.cache.RedisCache.deserializeCacheValue(RedisCache.java:260)
at org.springframework.data.redis.cache.RedisCache.lookup(RedisCache.java:94)
at org.springframework.cache.support.AbstractValueAdaptingCache.get(AbstractValueAdaptingCache.java:58)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doGet(AbstractCacheInvoker.java:73)
at org.springframework.cache.interceptor.CacheAspectSupport.findInCaches(CacheAspectSupport.java:554)
at org.springframework.cache.interceptor.CacheAspectSupport.findCachedItem(CacheAspectSupport.java:519)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:401)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
解决方案
遇到这种异常,一般很容易解决,使用 redis-cli keys 'xxx*' | xargs -n 1 redis-cli del
这个命令去把所有的这个缓存查出来并且删了就好了。
但是这是一个很容易出现的问题,每次都手工去避免比较麻烦。是不是能够通过程序实现,如果遇到这种异常,自动清除redis内的对应内容并自动执行对应的方法,不从redis取了呢(例如 Cacheable 注解的方法,执行方法返回结果并将返回内容重新放入缓存服务器),这个思路是可行的,通过配置一个自定义的 CacheErrorHandler来实现。
自定义的CacheErrorHandler
1 |
|
spring在redis里存储的key都是 User:1111 User:2222 这种类型,其中 User 是cache的name, 1111 / 2222 则是key,当pojo反序列化失败时,所有cache-name相同的条目都失效了,所以17行有 cache.clear() 把整个cache都清除了;如果不写这行,则每个key执行一次,从结果上看也没问题。
注册这个自定义个的CacheErrorHandler
1 |
|