事故原因

消息没有声明content_type属性,导致获取数据时失败

生产端

生产端用PHP语言开发,没有声明属性的content_type为text/plain,那么AMQP代理在底层就会默认将其视为字节数组。

消费端

消费端是由Java进行开发,因为没有声明content_type,Java开发的应用程序框架可能就将这个payload视为字节数组去处理,显然就会出错了。因为之前的约定是string格式,而不是字节数组。

AMQPMessage1.png

正确的做法应该在生产者投递消息的时候,加上消息属性“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”的字样)。

AMQPMessage2.png

所以涉及多系统的开发的时候(尤其多种不同程序语言),务必定义有效负载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

写在最后

写的都是一些自己在开发过程遇到的问题,绝非官方确切解释,有不对的地方欢迎指正并讨论,互相学习互相进步,才是目的。

xnkl
9 声望1 粉丝

讨厌不写习惯注释和空格的程序猿,很