在微服务开发过程中,为了使用方便经常会将频繁访问的信息如用户、权限等放置到SESSION中,便于服务访问,而且,微服务间为了共享SESSION,通常会使用Redis共享存储。但是这样就会有一个问题,Spring boot在封装Request对象时会将当前SESSION中所有属性对象反序列化,反序列化都成功以后,将SESSION对象生成。如果有一个微服务将本地的自定义Bean对象放置到SESSION中,则其他微服务都将出现反序列化失败,请求异常,服务将不能够使用了,这是一个灾难性问题。
如何解决这个问题呢?
需要了解一下Spring redis SESSION 是如何进行反序列化的。
Spring session针对Web的Request请求有一个org.springframework.session.web.http.SessionRepositoryFilter过滤器,根据SESSION ID获取相应的SESSION对象。
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter extends OncePerRequestFilter{
...
private final SessionRepository sessionRepository;
...
}
SessionRepositoryFilter会调用sessionRepository.findById(sessionId)来查找SESSION对象,对于Redis,sessionRepository的实现类为org.springframework.session.data.redis.RedisOperationsSessionRepository,通过查看源码,可以看到,该类默认的序列化类为org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.
public class RedisOperationsSessionRepository implements
FindByIndexNameSessionRepository,
MessageListener {
...
private RedisSerializer
通过查询JdkSerializationRedisSerializer源码,发现该类在反序列化时如果异常会抛出SerializationException异常,而SessionRepositoryFilter又没有处理异常,故如果序列化异常时就会导致请求异常。
public class JdkSerializationRedisSerializer implements RedisSerializer
这样我们就明白了异常的来源,那就想办法解决这个异常了,如何解决这个异常呢?定制Spring redis session的序列化类,替代原有的默认的JdkSerializationRedisSerializer。
网上也有好多关于Redis序列化定制的介绍,大部分经过验证对于Spring redis Session不起作用,而且有些定制太多,直接将sessionRepository进行了定制,这样是可以,但是感觉有点大材小用了,那应该如何定制SESSION满足系统场景需要呢?
通过查找发现,Github spring-session ISSUE中有类似定制反序列的答案,链接如下:
https://github.com/spring-projects/spring-session/issues/691
通过自定义springSessionDefaultRedisSerializer对象,将会替代默认的SESSION序列化对象。如何实现这个自定义的序列化对象呢?
这样需要回到上面我们提到的Session,Redis共享面临可能出现毁灭性事故的情形,某个微服务将自定义Bean对象保存到Session中导致其他微服务请求异常。所以,在实现定制的序列化对象时,对于反序列化失败时,一定不能抛出异常,避免系统请求中断。
如何做到自定义的序列化对象不抛异常呢?
@Component("springSessionDefaultRedisSerializer")
public class CustomSessionDefaultRedisSerializer extends JdkSerializationRedisSerializer {
private static final Logger LOG = LoggerFactory.getLogger(CustomSessionDefaultRedisSerializer.class);
public Object deserialize(@Nullable byte[] bytes) {
Object deserialObj = null;
try
{
deserialObj = super.deserialize(bytes);
}
catch(Exception e)
{
LOG.warn("deserialize session Object error!", e);
}
return deserialObj;
}
}
这是当前自己能力所限制,能够想到解决这个问题的方法,也许还有更好,更完美的方式,也可以大家一起讨论。