简介:学习 RabbitMQ 消息转换器,对比 SpringAMQP 默认 JDK 序列化问题,使用 Go 语言实现等效消息转换器,实现 Java 与 Go 跨语言收发消息。
一、案例需求
测试利用 SpringAMQP 发送对象类型的消息
- 声明一个队列,名为
object.queue - 编写单元测试,向队列中直接发送一条消息,消息类型为
Map - 在 RabbitMQ 管理控制台查看消息,总结存在的问题
核心知识点:消息转换器 MessageConverter SpringAMQP 中
MessageConverter负责两件事:
- 发送消息:Java 对象 → 字节数组(序列化)
- 消费消息:字节数组 → Java 对象(反序列化)
二、踩坑:SpringAMQP 默认序列化问题
SpringAMQP不手动配置 Jackson 转换器 时,默认使用SimpleMessageConverter,底层是JDK 原生序列化。
- 现象:发送 Map 对象后,MQ 控制台看到一串不可读的二进制长串(本例 129 字节)
- 缺点:
- 可读性极差,MQ 网页控制台无法直接阅读消息内容
- JDK 序列化是 Java 专属协议,跨语言无法解析(Go/Python 不能直接解码)
- 存在反序列化安全漏洞风险
解决方案:替换为
Jackson2JsonMessageConverter,使用 JSON 序列化,实现跨语言通信。
java
// Spring配置类,注入JSON消息转换器
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
三、Go 语言实现等效消息转换器
Go 语言的amqp091-go库没有内置消息转换器 ,需要我们手动封装序列化 / 反序列化逻辑,对标 Spring 的 MessageConverter。 原理:发送时json.Marshal对象转字节;消费时json.Unmarshal字节转回对象。
1. 安装依赖
Go
go get github.com/rabbitmq/amqp091-go
2. 生产者 send.go(声明队列 + 发送 Map 消息)
Go
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/rabbitmq/amqp091-go"
)
// 工具函数:错误捕获
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
func main() {
// 1. 连接RabbitMQ服务
conn, err := amqp091.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "连接MQ失败")
defer conn.Close()
// 2. 创建通道
ch, err := conn.Channel()
failOnError(err, "打开channel失败")
defer ch.Close()
// 3. 声明队列 object.queue(和Java案例保持一致)
q, err := ch.QueueDeclare(
"object.queue", // 队列名称
true, // durable:持久化队列
false, // delete when unused:队列不再使用时不删除
false, // exclusive:非独占队列
false, // no-wait:等待服务端响应
nil, // arguments:额外参数
)
failOnError(err, "声明队列失败")
// 4. 构造消息,等价Java Map<String,Object>
msg := map[string]any{
"name": "Jack",
"age": 21,
}
// 【消息转换器逻辑】JSON序列化,等同于Spring Jackson消息转换器
body, err := json.Marshal(msg)
failOnError(err, "json序列化失败")
// 5. 发送消息
err = ch.Publish(
"", // exchange:空,使用默认交换机,直接路由到队列
q.Name, // routingKey:队列名
false, // mandatory
false, // immediate
amqp091.Publishing{
ContentType: "application/json", // 标记消息类型为JSON,给消费者识别
Body: body,
})
failOnError(err, "发送消息失败")
fmt.Printf("消息发送成功: %s\n", body)
}
3. 消费者 receive.go(消费 object.queue,解析 Map 消息)
Go
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/rabbitmq/amqp091-go"
)
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
func main() {
// 建立连接
conn, err := amqp091.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "连接MQ失败")
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "打开channel失败")
defer ch.Close()
// 声明队列(生产者消费者都声明,保证队列一定存在)
q, err := ch.QueueDeclare(
"object.queue",
true,
false,
false,
false,
nil,
)
failOnError(err, "声明队列失败")
// 订阅队列消息
msgs, err := ch.Consume(
q.Name,
"",
true, // autoAck:自动确认消息
false,
false,
false,
nil,
)
failOnError(err, "注册消费者失败")
forever := make(chan bool)
// 协程持续监听消息
go func() {
for d := range msgs {
// 【消息转换器逻辑】JSON反序列化,字节数组转回map
var data map[string]any
err := json.Unmarshal(d.Body, &data)
if err != nil {
log.Println("解析消息失败:", err)
continue
}
fmt.Printf("收到消息: name=%v, age=%v\n", data["name"], data["age"])
}
}()
fmt.Println("等待消息...")
<-forever
}
四、封装通用 MessageConverter(进阶)
对标 Spring,封装通用转换器结构体,统一序列化 / 反序列化方法,方便复用:
Go
// MessageConverter 消息转换器,对标SpringAMQP MessageConverter
type MessageConverter struct{}
func NewMessageConverter() *MessageConverter {
return &MessageConverter{}
}
// ToBytes 对象转字节数组(发送时序列化)
func (c *MessageConverter) ToBytes(obj any) ([]byte, error) {
return json.Marshal(obj)
}
// FromBytes 字节数组转回对象(消费时反序列化)
func (c *MessageConverter) FromBytes(data []byte, target any) error {
return json.Unmarshal(data, target)
}
使用示例:
Go
converter := NewMessageConverter()
body, err := converter.ToBytes(msg)
// 消费时
var data map[string]any
err := converter.FromBytes(d.Body, &data)
五、核心总结
- SpringAMQP 默认使用 JDK 序列化,Java 专属二进制格式,Go 无法解析,MQ 控制台是乱码二进制。
- 推荐统一使用JSON 序列化,可读性好,天然支持跨语言(Java ↔ Go)。
- SpringAMQP 通过
MessageConverter完成对象与字节的转换;Go 没有内置实现,需要手动封装 json 序列化逻辑。 - 最佳实践:消息头带上
ContentType:application/json,让消费者识别消息编码格式。
六、运行测试步骤
- 启动 RabbitMQ 服务
- 终端 1 运行消费者:
go run receive.go,等待接收消息 - 终端 2 运行生产者:
go run send.go,发送消息 - 查看控制台输出,同时访问 RabbitMQ 管理页面,查看
object.queue消息(JSON 可读)