改版:
发送消息与订阅消息取消使用amqp提供的消息序列化与反序列化功能,使用String类型,发送消息时手动转化为json字符串再发送,消费消息时手动json反序列化。
背景:
如果使用自动序列化与反序列化功能,即给Rabbitmq配置Jackson2JsonMessageConverter消息转化器,当我们修改消息Body的java类型名称或者包名时,消费历史消息就会抛出ClassNotFoundException异常。
1、不做兼容上线,但需要:
- 确保不会有新的消息进入队列;
- 确保队列中的消息已经消费完。
2、粗暴方式,直接清空队列,丢弃历史消息;
3、做兼容,给旧消息创建一个类名匹配的消息Body类型,添加一个@RabbitHandler方法处理旧消息。
这是因为Rabbitmq为了实现一个队列支持多个方法消费(即@RabbitHandler注解的方法),每个方法消费不同Java类型的消息Body,在消费到消息时,就需要先反序列化出消息Body,才能根据消息Body的类型去匹配一个消费方法消费消息,如DelegatingInvocableHandler#invoke方法源码所示。
- // org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler#invoke
- public InvocationResult invoke(Message<?> message, Object... providedArgs) throws Exception {
- // 获取消息body类型
- Class<? extends Object> payloadClass = message.getPayload().getClass();
- // 匹配的方法
- InvocableHandlerMethod handler = getHandlerForPayload(payloadClass);
- // 调用方法消费
- Object result = handler.invoke(message, providedArgs);
- //....
- }
由于需要在匹配消息消费方法之前就需要解析出消息Body,也就是要先知道消息Body的Java类型才能实现json反序列化,这就要求消息生产者在发送消息时不得不在消息头添加一个参数表示消息Body的Java类型,如下图所示。
在消息消费阶段,Jackson2JsonMessageConverter也需要先根据消息头的TypeId获取JavaType,再执行反序列化操作,当类名修改时,或者生产者和消费者各自定义的类名不同,都将会导致反序列化失败。
除非确保消息Body的类名不会变,且生产者与消费者定义的完整类名相同,否则不建议使用自动序列化与反序列化功能。
本文转载自微信公众号「Java艺术」,作者wujiuye 。转载本文请联系Java艺术公众号。