事故原因
消息没有声明content_type属性,导致获取数据时失败
生产端
生产端用PHP语言开发,没有声明属性的content_type为text/plain,那么AMQP代理在底层就会默认将其视为字节数组。
消费端
消费端是由Java进行开发,因为没有声明content_type,Java开发的应用程序框架可能就将这个payload视为字节数组去处理,显然就会出错了。因为之前的约定是string格式,而不是字节数组。
正确的做法应该在生产者投递消息的时候,加上消息属性“content_type”,并声明为text/plain
$message = new AMQPMessage($this->body, ['content_type' => 'text/plain', 'delivery_mode' => 2]);
$this->channel->basic_publish($message, $this->bindings[$this->action]['exchangeName']);
其实加上这个'content_type' => 'text/plain'
,应该就是让PHP告诉rabbitmq,以字符串
的格式去存储消息,这样Java端的底层框架才能将数据正确转换为有效的字符串去处理,否则默认当成字节数组
处理。
(当然这个得看Java程序使用的框架,可能部分框架接口会把数据格式预处理成字符串,有些可能处理成字节数组,我猜最原始的形式也是字节数组)
总结经验
可能很多时候会忽略掉这个content_type,只记得把消息定义成持久化。因为单纯考虑PHP生产,PHP消费的情况不需要处理,可能是PHP底层的框架就已经实现了(可能AMQP扩展包就实现把payload默认转为字符串,你也可以看到在MQ管控台里get message的时候,会有个“Encoding: auto string/base64”的字样)。
所以涉及多系统的开发的时候(尤其多种不同程序语言),务必定义有效负载
的content_type
属性。其实涉及数据通信交互的场景里,都需要声明这样的一种契约,养成一个良好的习惯。
有效负载
在这里也了解一下有效负载。首先要知道AMQPMessage消息包括两部分,1消息属性 2有效负载。可类比http请求头和请求体理解。
1消息属性
列的是PHP版本的AMQP包定义属性名,其他程序语言可能定义不一样,但是在AMQP的范畴里是一致的)
'content_type' => 'shortstr',
'content_encoding' => 'shortstr',
'application_headers' => 'table_object', # 即web UI管控台里显示的header
'delivery_mode' => 'octet',
'priority' => 'octet',
'correlation_id' => 'shortstr',
'reply_to' => 'shortstr',
'expiration' => 'shortstr',
'message_id' => 'shortstr',
'timestamp' => 'timestamp',
'type' => 'shortstr',
'user_id' => 'shortstr',
'app_id' => 'shortstr',
'cluster_id' => 'shortstr',
2有效负载
(即web UI管控台里显示的payload),其实就是消息体的组成部分,但是在AMQP里面它不叫body,叫payload,但你可以理解为是消息体的意思。
AMQP代理将其视为不透明的字节数组(byte[]),也就是AMQP代理不会检查或者修改消息的有效负载。
AMQP消息可能只包含属性而没有有效负载,可能只有有效负载而没有属性,或者两者都没有,都是有可能的。
一般当没有设定content_type属性时,底层程序处理有效负载的默认类型应该就是字节数组(推断,没有真实测试验证)。
通常会使用序列化格式(如JSON,Thrift,Protocol Buffers和MessagePack)来序列化和结构化数据,
以便将其作为消息有效负载发布。在一般约定下,消息属性中的Content type和Content encoding一般可以表明其序列化的方式。
一般开发程序应用时会把生产者和消费者解耦开来,这里面可能牵涉到同一种程序语言和不同程序语言的问题。
有可能生产者是PHP开发,消费者程序由Java开发、Python开发等。这个时候声明消息的Content_type就尤为重要了。
这是一种契约,当发布消息没有对消息内容进行描述时,应用程序会倾向使用一种隐式契约去处理,这可能就会导致错误的产生。
引用参考
https://www.jianshu.com/p/48c57d3a6acd
https://juejin.im/post/5e3e1e54518825496452a31e
https://blog.csdn.net/Sadlay/article/details/86716028?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
写在最后
写的都是一些自己在开发过程遇到的问题,绝非官方确切解释,有不对的地方欢迎指正并讨论,互相学习互相进步,才是目的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。