RabbitMQ消费序列化问题

文章讲述了在使用RabbitMQ处理订单时遇到序列化问题,Spring默认的JDK序列化导致数据体积大且可读性差。作者建议采用JSON格式进行序列化和反序列化,并提供了引入JSON工具和重新配置MessageConverter的解决方案。

消费RabbitMQ消息时出现:Could not convert incoming message with content-type [application/x-java-serialized-object], 'json' keyword missing.的问题

今天在使用RabbitMQ消息队列实现异步处理订单问题时候出现了序列化的问题

先看下报错信息

可以看出来是实体对象的序列化出了问题

因为Spring采用的序列化的方式是JDK序列化。众所周知,JDK序列化存在以下问题:

  1. 数据体积过大
  2. 有安全漏洞
  3. 可读性差

分析一下出现问题的思路

既然是消费者消费时出现了序列化的问题,那我就先不消费,我先看下消息体和使用的序列化是什么样子。

配置RabbitMQ的基本信息

发布者代码

private static final String EXCHANGE_NAME = "voucher_user_order";	

@Resource
private RabbitTemplate rabbitTemplate;

@Test
void RabbitMQObjectTest(){
	VoucherOrder voucherOrder = new VoucherOrder();
	voucherOrder.setId(123456789L);
	// 2.4.用户id
	voucherOrder.setUserId(1L);
	// 2.5.优惠券id
	voucherOrder.setVoucherId(2L);
	// 这里一定注意中间加一个空字符串(routingkey),官网提供的源码需要交换机和消息体的方法必须包含routingkey
	rabbitTemplate.convertAndSend(EXCHANGE_NAME, "", voucherOrder);
}

消费者代码

因为我使用的是MQ的意图是用户下单,业务层审核用户符合下单条件,生成订单id并带着商品id和用户id封装为对象添加到队列,然后消费者通过发布订阅类型的广播(Fanout Exchange)模式来实现(交换机可以对应多个队列,来实现一个队列用于下单,一个队列用于下单成功短信通知客户)

使用Binding实现 交换机队列 进行绑定
/**
* user_order 队列:消费用户进行添加订单
* @return
*/
@Bean
public Queue fanoutQueue_user_order(){
	return new Queue("user_order");
}

/**
* user_message 队列:消费用户进行提醒用户下单成功
* @return
*/
@Bean
public Queue fanoutQueue_user_message(){
	return new Queue("user_message");
}

/**
* voucher_user_order 交换机与 user_order 队列 绑定
* @param fanoutQueue_user_order
* @param fanoutExchange_voucher_user_order
* @return
*/
@Bean
public Binding builderUserOrderVoucher(Queue fanoutQueue_user_order, FanoutExchange fanoutExchange_voucher_user_order){
	return BindingBuilder.bind(fanoutQueue_user_order).to(fanoutExchange_voucher_user_order);
}

/**
* voucher_user_order 交换机 与 user_order 队列 绑定
* @param fanoutQueue_user_message
* @param fanoutExchange_voucher_user_order
* @return
*/
@Bean
public Binding builderUserMessage(Queue fanoutQueue_user_message, FanoutExchange fanoutExchange_voucher_user_order){
	return BindingBuilder.bind(fanoutQueue_user_message).to(fanoutExchange_voucher_user_order);
}
消费代码

一定不要让消费者进行消费,先看下使用默认的JDK序列化会出现什么数据,先把RabbitMQListener给注释掉

//@RabbitListener(queues = "user_order")
public void consumerExchangeByUserOrderQueue(VoucherOrder voucherOrder){
	System.out.println("消息队列:user_order 接收到的消息为" + voucherOrder);
	System.out.println("完成异步将数据添加到数据库");
}

//@RabbitListener(queues = "user_message")
public void consumerExchangeByUserMessage(VoucherOrder voucherOrder){
	System.out.println("消息队列:user_message 接收到的消息为" + voucherOrder);
	System.out.println("完成异步将下单成功发送给客户手机号");
}

结果分析

上图,看结果

可以看到消息并未消费

接下来我们进去通过“Get Message(s)”查看到具体数据

显而易见序列化为:JDK序列化

数据的可读性非常差,况且占用空间很大

总结

Spring的消息对象的处理是由org.springframwork.amqp.support.converter.MessageConverter来处理的。而默认是SimpleMessageConverter,基于JDK的来完成序列化的。

如果要修改只需要定义一个MessageConverter类型的Bean即可。我认为目前最好的解决办法就是通过JSON的格式来做序列化和反序列化。

解决方案

1.引入JSON工具类的依赖

2.在publisher服务声明MessageConverter:注册Bean,归Spring管理,会覆盖掉自己已经有的Bean,从而实现此方法。

@Bean
    public MessageConverter jsonMessageConverter(){
        return new Jackson2JsonMessageConverter();
    }

3.在consumer服务声明MessageConverter(发送方与接收方必须使用相同的MessageConverter)

成功展示

做完上述工作可以看到已经成功将实体转为JSON格式进行传输

并且消费者可以接收并转换为对应的实体进行操作

小生很菜,大佬勿喷,有更好的办法可以讨论!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小张不嚣张Demo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值