# JMS(Java message service)
由Sun公司早期提出的消息标准,是一个Java平台中关于面向消息中间件的API,旨在为java应用提供统一的消息操作。
相关概念:
- 提供者:实现JMS规范的消息中间服务器
- 客户端:发送或接收消息的应用程序
- 生产者/发布者:创建并发送消息的客户端
- 消费者/订阅者:接收并处理消息的客户端
- 消息:应用程序之间传递的数据内容
JMS具有两种通信方式:(即点对点和发布订阅模型)
- Point-to-Point(P2P)
- Publish/Subscribe(Pub/Sub)
# 点对点模式
特点:
- 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中);
- 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列;
- 接收者在成功接收消息之后需向队列应答成功。
# 发布订阅模式
特点:
- 每个消息可以有多个消费者
- 发布者和订阅者之间有时间上的依赖性。针对某个主题(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来进行类比,不关心实现的语言,只要大家都按照相应的数据格式去发送报文请求
# 基础模型
工作过程:发布者(Publisher)发布消息(Message),经由交换机(Exchange)根据路由规则将收到的消息分发给与该交换机绑定的队列(Queue);最后Broker会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取
# 交换器
交换机拿到一个消息之后将它路由给一个或零个队列,有4种交换机:
# Direct exchange 直连
直连型交换机是根据消息携带的路由键(routing key)将消息投递给对应绑定键的队列:
- 将一个队列绑定到某个交换机上时,赋予该绑定一个绑定键(Binding Key),假设为R
- 当一个携带着路由键(Routing Key)为R的消息被发送给直连交换机时,交换机会把它路由给绑定键为R的队列
交换机与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 个队列
上图所示,生产者(P)生产消息 1 将消息 1 推送到 Exchange,由于 Exchange Type=fanout 这时候会遵循 fanout 的规则将消息推送到所有与它绑定 Queue,也就是图上的两个 Queue 最后两个消费者消费
# Topic exchange 主题
前面提到的 direct 规则是严格意义上的匹配,换言之 Routing Key 必须与 Binding Key 相匹配的时候才将消息传送给 Queue,而Topic 的路由规则是一种模糊匹配,可以通过通配符满足一部分规则就可以传送
当生产者发送消息 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 属性进行匹配。头交换机可以视为直连交换机的另一种表现形式。但直连交换机的路由键必须是一个字符串,而头属性值则没有这个约束,它们甚至可以是整数或者哈希值(字典):
- 绑定一个队列到头交换机上时,会同时绑定多个用于匹配的头(header)
- 传来的消息会携带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功能 |