我们可以把消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用。消息队列是分布式系统中重要的组件,目前使用较多的消息队列有 ActiveMQ
,RabbitMQ
,Kafka
,RocketMQ
。
如果模块之间不直接进行调用,模块之间耦合度就会很低,那么修改一个模块或者新增一个模块对其它模块的影响会很小,从而实现可扩展性。
通过使用消息队列,一个模块只需要向消息队列中发送消息,其它模块可以选择性地从消息队列中订阅消息从而完成调用。
在此业务环境下,消息队列使利用 ==发布-订阅模式== 工作。消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计。
💡
发布-订阅模式
是 JMS 规范 两种消息模型中的一种,另外 AMQP 协议 还提出了五种消息模型,见下文
另外为了避免消息队列服务器宕机造成消息丢失,会将成功发送到消息队列的消息存储在消息生产者服务器上,等消息真正被消费者服务器处理后才删除消息。在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中的其他服务器发布消息。
先来看传统同步的情况,举个例子:系统 A 接收一个用户请求,需要进行写库操作,还需要同样的在 B、C、D 三个系统中进行写库操作。如果 A 自己本地写库只要 1ms,而 B、C、D 三个系统写库分别要 100ms、200ms、300ms。最终请求总延时是 1 + 100 + 200 + 300 = 601ms,用户体验大打折扣。
如果使用消息队列,那么系统 A 就只需要发送 3 条消息到消息队列中就行了,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 1 + 5 = 6ms,对于用户而言,体验好感度直接拉满。
如果没有使用缓存或者消息队列,那么系统就是直接基于数据库 MySQL 的,如果有那么一个高峰期,产生了大量的请求涌入 MySQL,毫无疑问,系统将会直接崩溃。
那如果我们使用消息队列,假设 MySQL 每秒钟最多处理 1k 条数据,而高峰期瞬间涌入了 5k 条数据,不过,这 5k 条数据涌入了消息队列。这样,我们的系统就可以从消息队列中根据数据库的能力慢慢的来拉取请求,不要超过自己每秒能处理的最大请求数量就行。
也就是说消息队列每秒钟 5k 个请求进来,1k 个请求出去,假设高峰期 1 个小时,那么这段时间就可能有几十万甚至几百万的请求积压在消息队列中。不过这个短暂的高峰期积压是完全可以的,因为高峰期过了之后,每秒钟就没有那么多的请求进入消息队列了,但是数据库依然会按照每秒 1k 个请求的速度处理。所以只要高峰期一过,系统就会快速的将积压的消息给处理掉。
因为用户请求数据写入消息队列之后就立即返回给用户了,但是请求数据在后续的业务校验、写数据库等操作中可能失败。因此使用消息队列进行异步处理之后,需要适当修改业务流程进行配合,比如用户在提交订单之后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单之后,甚至出库后,再通过电子邮件或短信通知用户订单成功,以免交易纠纷。这就类似我们平时手机订火车票和电影票。
- 系统可用性降低: 在加入MQ之前,你不用考虑消息丢失或者说 MQ 挂掉等等的情况,但是,引入MQ之后你就需要去考虑了
- 系统复杂性提高: 加入MQ之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题
- 一致性问题: 消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息,就会导致数据不一致的情况
JMS(JAVA Message Service, java消息服务)
是 java 的消息服务,JMS 的客户端之间可以通过 JMS 服务进行异步的消息传输。JMS(JAVA Message Service,Java消息服务)API 是一个消息服务的标准或者说是规范,允许应用程序组件基于 JavaEE 平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
ActiveMQ
就是基于 JMS 规范实现的。
JMS 定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。
StreamMessage
-- Java原始值的数据流MapMessage
--一套名称-值对TextMessage
--一个字符串对象ObjectMessage
--一个序列化的 Java对象BytesMessage
--一个字节的数据流
一个生产者向一个特定的队列发布消息,只有一个消费者从该队列中读取消息;
生产者不需要在接收者消费该消息期间处于运行状态,接收者也同样不需要在消息发送时处于运行状态;每一个成功处理的消息都由接收者签收;多个消费者对于队列内的消息是竞争消费关系,每个消费者只能收到队列中的一部分消息。
一个生产者向一个特定的队列发布消息,0 个或多个订阅者可以接受来自特定消息主题的消息;
发布者需要创建一个订阅主题(Topic)
以便客户能够购订阅并保持持续的活动以接受消息,一个订阅主题是由至少一个队列(Queue)组成的,除非订阅者创建了持久的订阅,在订阅者未连接时发布的消息将在订阅者重新连接时重新发布,每个消费者都能收到全量的消息。
AMQP,即 Advanced Message Queuing Protocol
,一个提供统一消息服务的应用层标准 高级消息队列协议(二进制应用层协议),是应用层协议的一个开放标准, 为面向消息的中间件设计,兼容 JMS。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件同产品,不同的开发语言等条件的限制。
RabbitMQ
就是基于 AMQP 协议实现的。
AMQP 提供了五种消息模型:
- direct exchange
- fanout exchange
- topic change
- headers exchange
- system exchange
本质来讲,后四种和 JMS 的 Pub/Sub 模型没有太大差别,仅是在路由机制上做了更详细的划分
对比方向 | JMS | AMQP |
---|---|---|
定义 | Java API | 协议 |
跨语言 | 否 | 是 |
跨平台 | 否 | 是 |
支持消息类型 | 提供两种消息模型:①Peer-2-Peer;②Pub/sub | 提供了五种消息模型:①direct exchange;②fanout exchange;③topic change;④headers exchange;⑤system exchange。本质来讲,后四种和JMS的pub/sub模型没有太大差别,仅是在路由机制上做了更详细的划分; |
支持消息类型 | 支持 5 种消息类型 | byte[](二进制) |
总结:
- AMQP 为消息定义了线路层(wire-level protocol)的协议,而 JMS 所定义的是 API 规范。在 Java 体系中,多个 client 均可以通过 JMS 进行交互,不需要应用修改代码,但是其对跨平台的支持较差。而 AMQP 天然具有跨平台、跨语言特性。
- JMS 支持 TextMessage、MapMessage 等复杂的消息类型;而 AMQP 仅支持 byte[] 消息类型(复杂的类型可序列化后发送)。
- 由于 Exchange 提供的路由算法,AMQP 可以提供多样化的路由方式来传递消息到消息队列,而 JMS 仅支持 队列 和 主题/订阅 方式两种。
对比方向 | 概要 |
---|---|
吞吐量 | 万级的 ActiveMQ 和 RabbitMQ 的吞吐量(ActiveMQ 的性能最差)要比 十万级甚至是百万级的 RocketMQ 和 Kafka 低一个数量级。 |
可用性 | 都可以实现高可用。ActiveMQ 和 RabbitMQ 都是基于主从架构实现高可用性。RocketMQ 基于分布式架构。 kafka 也是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
时效性 | RabbitMQ 基于 erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。其他三个都是 ms 级。 |
功能支持 | 除了 Kafka ,其他三个功能都较为完备。 Kafka 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准 |
消息丢失 | ActiveMQ 和 RabbitMQ 丢失的可能性非常低, RocketMQ 和 Kafka 理论上不会丢失。 |
总结:
-
😥
ActiveMQ
的社区算是比较成熟,但是较目前来说,ActiveMQ
的性能比较差,而且版本迭代很慢,不推荐使用。 -
RabbitMQ
在吞吐量方面虽然稍逊于Kafka
和RocketMQ
,但是由于它基于 erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。但是也因为RabbitMQ
基于 erlang 开发,所以国内很少有公司有实力做 erlang 源码级别的研究和定制。👍 如果业务场景对并发量要求不是太高(十万级、百万级),那这四种消息队列中,
RabbitMQ
一定是你的首选。👍 如果是大数据领域的实时计算、日志采集等场景,用
Kafka
是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。 -
RocketMQ
阿里出品,Java 系开源项目,源代码我们可以直接阅读,然后可以定制自己公司的 MQ,并且RocketMQ
有阿里巴巴的实际业务场景的实战考验。RocketMQ
社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准 JMS 规范走的有些系统要迁移需要修改大量代码。还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险. -
kafka
的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms 级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时kafka
最好是支撑较少的 topic 数量即可,保证其超高吞吐量。kafka
唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性,天然适合大数据实时计算以及日志收集。