JMS 与 AMQP 协议

2023/3/11

# JMS(Java message service)

由Sun公司早期提出的消息标准,是一个Java平台中关于面向消息中间件的API,旨在为java应用提供统一的消息操作。

相关概念:

  • 提供者:实现JMS规范的消息中间服务器
  • 客户端:发送或接收消息的应用程序
  • 生产者/发布者:创建并发送消息的客户端
  • 消费者/订阅者:接收并处理消息的客户端
  • 消息:应用程序之间传递的数据内容

JMS具有两种通信方式:(即点对点和发布订阅模型)

  • Point-to-Point(P2P)
  • Publish/Subscribe(Pub/Sub)

# 点对点模式

image.png

特点:

  • 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中);
  • 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列;
  • 接收者在成功接收消息之后需向队列应答成功。

# 发布订阅模式

image.png

特点:

  • 每个消息可以有多个消费者
  • 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。
  • 为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),当它复活后,它也能接收到发布者的消息。

# AMQP(高级消息队列协议)

AMQP(advanced message queuing protocol)在2003年时被提出,最早用于解决金融领不同平台之间的消息传递交互问题。顾名思义,AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。这使得实现了AMQP的provider天然性就是跨平台的。意味着我们可以使用Java的AMQP provider,同时使用一个python的producer加一个rubby的consumer。从这一点看,AQMP可以用http来进行类比,不关心实现的语言,只要大家都按照相应的数据格式去发送报文请求

# 基础模型

image.png

工作过程:发布者(Publisher)发布消息(Message),经由交换机(Exchange)根据路由规则将收到的消息分发给与该交换机绑定的队列(Queue);最后Broker会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取

# 交换器

交换机拿到一个消息之后将它路由给一个或零个队列,有4种交换机: image.png

# Direct exchange 直连

直连型交换机是根据消息携带的路由键(routing key)将消息投递给对应绑定键的队列:

  1. 将一个队列绑定到某个交换机上时,赋予该绑定一个绑定键(Binding Key),假设为R
  2. 当一个携带着路由键(Routing Key)为R的消息被发送给直连交换机时,交换机会把它路由给绑定键为R的队列

20181022130416336.png

交换机与Queue1的绑定key是{booking},与Queue2的绑定key是{create,booking,confire}

  • 当生产者发送消息时 Rotuing key=booking 时,这时候将消息传送给 Exchange,Exchange 获取到生产者发送过来消息后,会根据自身的规则进行与匹配相应的 Queue,这时发现 Queue1 和 Queue2 都符合,就会将消息传送给这两个队列
  • 如果我们以 Rotuing key=create 和 Rotuing key=confirm 发送消息时,这时消息只会被推送到 Queue2 队列中,其他 Routing Key 的消息将会被丢弃

# Fanout exchange 扇形

扇型交换机将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键。如果 N 个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的 N 个队列 20181022130646170.png

上图所示,生产者(P)生产消息 1 将消息 1 推送到 Exchange,由于 Exchange Type=fanout 这时候会遵循 fanout 的规则将消息推送到所有与它绑定 Queue,也就是图上的两个 Queue 最后两个消费者消费

# Topic exchange 主题

前面提到的 direct 规则是严格意义上的匹配,换言之 Routing Key 必须与 Binding Key 相匹配的时候才将消息传送给 Queue,而Topic 的路由规则是一种模糊匹配,可以通过通配符满足一部分规则就可以传送 20181022131252616.png

当生产者发送消息 Routing Key=F.C.E 的时候,这时候只满足 Queue1,所以会被路由到 Queue 中,如果 Routing Key=A.C.E 这时候会被同是路由到 Queue1 和 Queue2 中,如果 Routing Key=A.F.B 时,这里只会发送一条消息到 Queue2 中

# Headers exchange 头

headers 类型的 Exchange 不依赖于 routing key 与 binding key 的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。头交换机可以视为直连交换机的另一种表现形式。但直连交换机的路由键必须是一个字符串,而头属性值则没有这个约束,它们甚至可以是整数或者哈希值(字典):

  1. 绑定一个队列到头交换机上时,会同时绑定多个用于匹配的头(header)
  2. 传来的消息会携带header,以及会有一个 “x-match” 参数。当 “x-match” 设置为 “any” 时,消息头的任意一个值被匹配就可以满足条件,而当 “x-match” 设置为 “all” 的时候,就需要消息头的所有值都匹配成功

# Java JMS 与 AMQP 对比

JMS AMQP
定义 Java api protocol
跨语言
跨平台
模型 提供两种消息模型:Peer-2-Peer、Pub/sub 提供四种:direct、fanout、topic、header
支持消息类型 多种消息类型:TextMessage MapMessage BytesMessage StreamMessage ObjectMessage Message (只有消息头和属性) byte[] 当实际应用时,有复杂的消息,可以将消息序列化后发送
综合评价 JMS 定义了JAVA API层面的标准;在java体系中,多个client均可以通过JMS进行交互,不需要应用修改代码,但是其对跨平台的支持较差 AMQP定义了协议层的协议标准;天然具有跨平台、跨语言特性

其他消息队列协议:MQTT、STOMP、XMPP

# 目前市面上的消息队列

消息队列 实现
ActiveMQ 基于JMS实现
RabbitMQ 基于AMQP实现
Redis、Kafka、RocketMQ 自身需要未严格遵循MQ规范,而是基于TCP/IP自行封装了一套协议,通过网络Socket接口进行传输,实现了MQ功能