消息中间件简介
在高并发、高消息吞吐的互联网场景中,我们经常会使用消息队列(Message Queue)作为基础设施,在服务端架构中担当消息中转、消息削峰、事务异步处理等职能。
消息队列中间件(简称消息中间件)是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下提供应用解耦、弹性伸缩、冗余存储、流量削峰、异步通信、数据同步等等功能,其作为分布式系统架构中的一个重要组件,有着举足轻重的地位。
消息中间件的基本原理
消息中间件的基本概念
- Broker: 消息服务器,作为Server提供消息核心服务。
- Producer: 消息生产者,业务的发起方,负责生产消息传输给 Broker。
- Consumer: 消息消费者,业务的处理方,负责从Broker获取消息并进行业务逻辑处理。
- Topic: 主题,发布/订阅模式下的消息统一汇集地,不同生产者向 Topic 发送消息,由MQ服务器分发到不同的订阅者,实现消息的广播。
- Queue: 队列,PTP模式下,特定生产者向特定queue发送消息,消费者订阅特定的queue完成指定消息的接收。
- Message: 消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输。
消息中间件的模式分类
PTP点对点
使用queue作为通信载体,消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。消息被消费以后,queue中不再存储,所以消息消费者不可能消费到已经被消费的消息。 Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
点对点模式的特点是:
- 每个消息只会被一个消费者消费
- 发送者和接收者之间在时间上没有依赖性
- 接收者在成功接收消息之后需向队列应答成功
- 利用FIFO先进先出的特性,可以保证消息的顺序性
Pub/Sub发布订阅(广播)
使用topic作为通信载体,消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。 queue实现了负载均衡,将producer生产的消息发送到消息队列中,由多个消费者消费。但一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有一个可用的消费者。
Pub/Sub发布订阅的特点是:
- 每个消息可以有多个消费者:和点对点方式不同,发布消息可以被所有订阅者消费
- 发布者和订阅者之间有时间上的依赖性
- 针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息
- 为了消费消息,订阅者必须保持运行的状态
主流MQ介绍
目前开源的消息中间极其丰富的,比如 ActiveMQ、RabbitMQ、Kafka、RocketMQ、ZeroMQ 等。但是业务场景不一样,受限于系统的规模,业务的取舍(如功能需求、延迟容忍度、消息回溯、可持久化需求),就需要从不同的角度去衡量为业务选择更适合的消息队列。目前比较主流MQ有:RabbitMQ、RocketMQ、Kafka。
- RabbitMQ
采用 Erlang 语言实现的 AMQP 协议的消息中间件,起源于金融系统,广泛应用在分布式系统中,承担消息转发的职责。RabbitMQ 发展历史比较久远,影响范围比较大,被很多开发者认可,在可靠性、可用性、可扩展性、功能性方面有着非凡表现。 - RocketMQ
阿里开源的消息中间件,目前已经捐献给 Apache 基金会,它是由 Java 语言开发的,具备高吞吐量、高可用性、适合大规模分布式系统应用等特点。并且在阿里的双11、618等重要活动中经受住了考验。 - Kafka
起初是由 LinkedIn 公司采用 Scala 语言开发的一个分布式、多分区、多副本且基于 zookeeper 协调的分布式消息系统,现已捐献给 Apache 基金会。它是一种高吞吐量的分布式发布订阅消息系统,以可水平扩展和高吞吐率而被广泛使用。目前越来越多的开源分布式处理系统如 Cloudera、Apache Storm、Spark、Flink 等都支持与 Kafka 集成。
主流MQ对比
特性 | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|
开发语言 | erlang | java | scala、java |
支持协议 | AMQP | 自定义 | 基于TCP 自定义 |
消息存储能力 | 内存、磁盘。支持少量堆积。 | 磁盘。支持大量堆积。 | 内存、磁盘、数据库。支持大量堆积。 |
消息事务性 | 支持(信道设置事务模式,性能有影响) | 支持 | 支持 |
单机吞吐量 | 万级 | 10万级+ | 10万级+ |
时效性 | us级 | ms级 | ms级以内 |
消息重复 | 支持at least once、at most once | 支持at least once | 支持at least once、at most once、exactly once |
消息回溯 | 不支持 | 支持指定时间点和offset的回溯 | 支持指定分区offset位置的回溯 |
消息重试 | 不支持,但可以设置autoACK=false,未收到确认的会重入队列 | 支持 | 不支持,但可以通过消息回溯的方式来实现 |
可用性 | 高(主从架构) | 非常高(分布式架构) | 非常高(分布式架构) |
功能特性说明 | 基于erlang开发,所以并发能力很强,性能极其好,延时很低; | 管理界面较丰富 | MQ功能比较完备,扩展性佳 |
消息中间件技术选型
-
系统的建设规模
- RocketMQ:适用于中等规模以上的系统建设。在阿里巴巴内部,RocketMQ 被广泛应用于大规模的电商系统中,经受住了高并发、大数据量的考验。对于一般企业级应用,如果系统规模不是特别巨大,RocketMQ 能够提供稳定可靠的消息服务,并且在资源占用和部署复杂度上相对较为合理。
- Kafka:更适合超大规模的系统建设。由于其设计初衷是为了处理海量的日志数据,因此在大规模分布式系统中表现出色。对于数据量巨大、吞吐量要求极高的场景,如日志收集、实时数据分析等,Kafka 是一个非常强大的选择。
-
功能层面
功能 RocketMQ Kafka 顺序消息 支持严格的消息顺序性。在某些业务场景下,比如订单处理、交易系统等,消息的顺序非常重要。RocketMQ 可以通过分区(Queue)的方式保证同一分区内的消息严格按照发送顺序被消费。 在默认情况下,Kafka 只能保证分区内的消息顺序,不能保证全局的消息顺序。但我们可以通过重写客户端的分区器来实现将某类消息发送到同一个分区内部,但是需要考虑下消息堆积的问题。 事务消息 提供事务消息功能,可以保证分布式事务的最终一致性。在涉及多个系统之间的数据交互和事务处理时,RocketMQ 的事务消息非常有用。 支持事务,但相对来说事务的概念比较弱,RocketMQ 的事务消息功能更加成熟和易用。 定时消息 支持定时消息,可以在指定的时间点发送消息。这个功能在一些需要定时执行任务的场景下非常方便,比如定时推送通知、定时结算等。 不支持。目前没有原生的定时消息功能,需要通过外部的定时任务框架来实现。 批量消息 支持,以同步的发送方式避免消息丢失 支持,以异步的方式进行发送 消息广播 支持 不支持 消息过滤 支持,并且是基于SQL92的标准通过表达式实现 支持,基于KafkaStreams的方式完成消息的过滤 消息追踪 支持 不支持 消息回溯 支持以时间和位移的方式回溯消息 仅支持位移回溯消息 -
性能层面
- 吞吐量
- RocketMQ:在一般情况下,RocketMQ 的吞吐量也非常高,可以满足大多数企业级应用的需求。在阿里巴巴的实际应用中,RocketMQ 可以达到每秒数十万条消息的吞吐量。
- Kafka:以高吞吐量著称,尤其在处理大量数据时表现出色。在大规模分布式系统中,Kafka 的吞吐量可以轻松达到每秒数百万条消息甚至更高。
- 延迟
- RocketMQ:延迟相对较低,一般在毫秒级别。对于实时性要求较高的业务场景,RocketMQ 可以提供较好的响应速度。
- Kafka:延迟也比较低,但在某些情况下可能会略高于 RocketMQ。不过,对于大多数业务场景来说,Kafka 的延迟也是可以接受的。
在性能方面其实消息服务器(Broker)对消息的处理方式是最主要的因素,而消息的处理方式最重要的就是消息的存储结构。在这一方面RocketMQ参考了Kafka的存储结构,虽说RocketMQ进一步做了优化,但两者间的性能差距在日常使用的情况下并不会太大。
- 吞吐量
-
高可用层面
- RocketMQ:采用主从架构,通过同步或异步复制的方式保证数据的高可用性。当主节点出现故障时,从节点可以自动切换为主节点,继续提供服务。同时,RocketMQ 还支持多副本机制,可以进一步提高系统的可靠性。最重要的是RocketMQ在搭建高可用集群时不需要引入外部的中间件。
- Kafka:同样采用主从架构,通过副本机制实现高可用。当主节点出现故障时,从节点可以自动切换为主节点。Kafka 的副本机制相对较为复杂,但在大规模分布式系统中可以提供非常高的可用性。在Kafka的旧版本中需要引入Zookeeper完成分布式协调功能,但是在较新的版本中,Kafka也将Zookeeper的功能迁移至其内部,降低了外部依赖。
-
运维管理层面
- RocketMQ:部署相对较为简单,提供了丰富的管理工具和监控指标,可以方便地进行运维管理。值得一提的是Rocketmq的配置要简洁很多,容易配置,对使用者来说比较友好。
- Kafka:部署和管理相对复杂一些,需要对其架构和配置有深入的了解。不过,Kafka 也提供了一些强大的管理工具和监控插件,可以帮助运维人员进行管理。Kafka的配置非常丰富,通过合理的配置可以获得极高的性能,但是配置不易被掌握,通常需要对照官网去解读配置的作用。
-
社区力度及生态发展
- RocketMQ:由阿里巴巴开源,社区活跃度较高,有很多企业和开发者在使用和贡献。同时,阿里巴巴也提供了专业的技术支持。
- Kafka:作为一个非常成熟的开源项目,社区非常活跃,有大量的文档、教程和案例可供参考。并且,有很多知名的公司在使用 Kafka,社区的支持力度非常大。
在这里没有提及RabbitMQ,主要是因为RabbitMQ的开发语言是Erlang,而非Java生态。如果有定制化需求的时候,那团队的使用成本就比较高。而且在业务功能需求没有特别差异化的情况下,我们当然会更喜欢可支持业务规模更大的中间件以应对未来可能的业务拓展。
如果有实际的业务体量需求,比如足够大规模的分布式环境,以及足够大的数据量。这时候 RocketMQ 和 Kafka 都是10w+的吞吐量,都可以在考虑范围内。如果仅是基于业务需求考虑,则优选RocketMQ。而如果是基于日志采集或大数据分析场景下优选Kafka,毕竟Kafka在日志上报、监控数据采集方面有着大规模的实践经验,这也是Kafka主打的应用场景。
具体该选哪个,不仅仅只是看消息队列的功能或者其使用场景。需要从多个层面去衡量取舍。也不是一篇文章就能说的清楚的。比如我们还需要考虑团队的技术储备、现有系统的生态等方面因素。可能唯一不变的标准就是:在满足业务功能的情况下,将使用成本尽可能小,使用成本包括:开发团队的学习成本、消息中间件的维护成本、接入现有系统的改造成本等。