消息队列
消息队列(Message Queue)是一种应用程序间的通信机制,用于让不同的应用程序通过统一的消息传输来交换信息。 消息队列的主要特征包括:
- 异步(Asynchronous) - 发送方可以立即返回,不需要等到消息被处理。这可以提高系统的整体响应速度。
- 解耦(Decoupled) - 发送方和接收方不需要知道对方的存在。这可以降低系统之间的耦合度。
- 可靠(Reliable) - 消息队列会确保消息能可靠地到达接收方,即使接收方暂时不可用也可以保存消息。
- 扩展性(Scalable) - 可以根据消息量轻松地扩展消息队列的处理能力。
- 传输保证(Guaranteed Delivery) - 消息队列会确保每条消息都被传输和处理,不会出现消息丢失的情况。
- 支持多种协议 - 常见的消息队列可以支持 AMQP、MQTT、STOMP 等标准协议。
消息队列的用途包括削峰填谷、异步处理、解耦服务等。流行的消息队列实现包括 RabbitMQ、Kafka、ActiveMQ 等。总体来说,使用消息队列可以提高系统的可扩展性、可靠性和灵活性。
应用场景
- 异步处理
- 应用场景,例如用户注册完账号后,需要邮件和短信通知用户。传统的方式是同步处理的,使用中间件就可以实现异步处理,提供系统处理效率。
- 流量削峰
- 流量削峰就是某个时间段流量特别高,需要特殊处理。例如:淘宝双十一、秒杀获得、抢购活动。这个时候流量特别高,可以把用户请求发送到消息中间件排队处理,这样既可以系统压力,又不会让消息丢失。
- 应用解耦
- 在常见的订单支付场景中,为了鼓励用户多下单,运营经常会搞各类促销活动。当用户订单支付(订单服务)成功后,就送用户XXX优惠券(优惠券服务);给用户增加积分(积分服务)等等。
- 日志处理
- 在一个大型应用的开发中,应用程序通常部署几台,甚至几十台应用服务器上。此时,开发人员和运维人员查询日志时,需要到服务对应的服务器上,去跟踪无疑是相当的不方便。此时,需要一个集中化的日志管理系统,现在比较流行的方案就是ELK(elasticsearch、logstash、kibana) +Kakfa的方案。
- 分布式事务
- 分布式事务就是实现跨数据库的事务支持
常用协议
- JMS协议
- JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
- AMQP协议
- AMQP(Advanced Message Queuing Protocol),一个提供统一消息服务,应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。
- 优点:可靠、通用
- MQTT协议
- MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。
- 优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统
- STOMP协议
- STOMP(Streaming Text Orientated Message Protocol)是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。
- 优点:命令模式(非topic\queue模式)
- XMPP协议
- XMPP(可扩展消息处理现场协议,Extensible Messaging and Presence Protocol)是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。
- 优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大
- 其他基于TCP/IP自定义的协议
- 有些特殊框架(如:redis、kafka、zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCP/IP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能。
常见的消息队列
RabbitMQ
RabbitMQ于2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
- 主要特性
- 可靠性:提供了多种技术可以让你在性能和可靠性之间进行权衡。这些技术包括持久性机制、投递确认、发布者证实和高可用性机制;
- 灵活的路由:消息在到达队列前是通过交换机进行路由的。RabbitMQ为典型的路由逻辑提供了多种内置交换机类型。如果你有更复杂的路由需求,可以将这些交换机组合起来使用,你甚至可以实现自己的交换机类型,并且当做RabbitMQ的插件来使用;
- 消息集群:在相同局域网中的多个RabbitMQ服务器可以聚合在一起,作为一个独立的逻辑代理来使用;
- 队列高可用:队列可以在集群中的机器上进行镜像,以确保在硬件问题下还保证消息安全;
- 支持多种协议:支持多种消息队列协议;
- 支持多种语言:用Erlang语言编写,支持只要是你能想到的所有编程语言;
- 管理界面:RabbitMQ有一个易用的用户界面,使得用户可以监控和管理消息Broker的许多方面;
- 跟踪机制:如果消息异常,RabbitMQ 提供消息跟踪机制,使用者可以找出发生了什么;
- 插件机制:提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。
- 优点
- 由于Erlang语言的特性,消息队列性能较好,支持高并发;
- 健壮、稳定、易用、跨平台、支持多种语言、文档齐全;
- 有消息确认机制和持久化机制,可靠性高;
- 高度可定制的路由;
- 管理界面较丰富,在互联网公司也有较大规模的应用,社区活跃度高。
- 缺点
- 尽管结合 Erlang 语言本身的并发优势,性能较好,但是不利于做二次开发和维护;
- 实现了代理架构,意味着消息在发送到客户端之前可以在中央节点上排队。此特性使得RabbitMQ易于使用和部署,但是使得其运行速度较慢,因为中央节点 增加了延迟,消息封装后也比较大;需要学习比较复杂的接口和协议,学习和维护成本较高。
RocketMQ
RocketMQ出自阿里的开源产品,用Java语言实现,在设计时参考了Kafka,并做出了自己的一些改进,消息可靠性上比Kafka更好。RocketMQ在阿里内部被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。
- 主要特性
- 基于 队列模型:具有高性能、高可靠、高实时、分布式等特点;
- Producer、Consumer、队列都支持分布式;
- Producer向一些队列轮流发送消息,队列集合称为Topic。Consumer如果做广播消费,则一个Consumer实例消费这个Topic对应的所有队列;如果做集群消费,则多个Consumer 实例平均消费这个Topic对应的队列集合;
- 能够保证严格的消息顺序;
- 提供丰富的消息拉取模式;
- 高效的订阅者水平扩展能力;
- 实时的消息订阅机制;
- 亿级消息堆积 能力;
- 较少的外部依赖。
- 优点
- 单机支持1万以上持久化队列;
- RocketMQ的所有消息都是持久化的,先写入系统PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据,而访问时,直接从内存读取。
- 模型简单,接口易用(JMS的接口很多场合并不太实用);
- 性能非常好,可以允许大量堆积消息在Broker中;
- 支持多种消费模式,包括集群消费、广播消费等;
- 各个环节分布式扩展设计,支持主从和高可用;
- 开发度较活跃,版本更新很快。
- 缺点
- 支持的 客户端语言不多,目前是Java及C++,其中C++还不成熟;
- RocketMQ社区关注度及成熟度也不及前两者;
- 没有Web管理界面,提供了一个 CLI (命令行界面) 管理工具带来查询、管理和诊断各种问题;
- 没有在MQ核心里实现JMS等接口;
ActiveMQ
ActiveMQ是由Apache出品,ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。
- 主要特性
- 服从JMS规范:JMS 规范提供了良好的标准和保证,包括:同步 或 异步 的消息分发,一次和仅一次的消息分发,消息接收和订阅等等。遵从JMS规范的好处在于,不论使用什么JMS实现提供者,这些基础特性都是可用的;
- 连接灵活性:ActiveMQ提供了广泛的连接协议,支持的协议有:HTTP/S,IP多播,SSL,TCP,UDP等等。对众多协议的支持让ActiveMQ拥有了很好的灵活性;
- 支持的协议种类多:OpenWire、STOMP、REST、XMPP、AMQP;
- 持久化插件和安全插件:ActiveMQ提供了多种持久化选择。而且,ActiveMQ的安全性也可以完全依据用户需求进行自定义鉴权和授权;
- 支持的客户端语言种类多:除了Java之外,还有:C/C++,.NET,Perl,PHP,Python,Ruby;
- 代理集群:多个ActiveMQ代理可以组成一个集群来提供服务;
- 异常简单的管理:ActiveMQ是以开发者思维被设计的。所以,它并不需要专门的管理员,因为它提供了简单又实用的管理特性。有很多种方法可以监控ActiveMQ不同层面的数据,包括使用在JConsole或者在ActiveMQ的WebConsole中使用JMX。通过处理JMX的告警消息,通过使用命令行脚本,甚至可以通过监控各种类型的日志。
- 优点
- 跨平台(JAVA编写与平台无关,ActiveMQ几乎可以运行在任何的JVM上);
- 可以用JDBC:可以将数据持久化到数据库。虽然使用JDBC会降低ActiveMQ的性能,但是数据库一直都是开发人员最熟悉的存储介质;
- 支持JMS规范:支持JMS规范提供的统一接口;
- 支持自动重连和错误重试机制;
- 有安全机制:支持基于shiro,jaas等多种安全配置机制,可以对Queue/Topic进行认证和授权;
- 监控完善:拥有完善的监控,包括WebConsole,JMX,Shell命令行,Jolokia的RESTful API;
- 界面友善:提供的WebConsole可以满足大部分情况,还有很多第三方的组件可以使用,比如hawtio;
- 缺点
- 社区活跃度不及RabbitMQ高;
- 根据其他用户反馈,会出莫名其妙的问题,会丢失消息;
- 目前重心放到activemq6.0产品Apollo,对5.x的维护较少;
- 不适合用于上千个队列的应用场景;
Kafka
Apache Kafka是一个分布式消息发布订阅系统。它最初由LinkedIn公司基于独特的设计实现为一个分布式的日志提交系统(a distributed commit log),之后成为Apache项目的一部分。Kafka性能高效、可扩展良好并且可持久化。它的分区特性,可复制和可容错都是其不错的特性。
- 主要特性
- 快速持久化:可以在O(1)的系统开销下进行消息持久化;
- 高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
- 完全的分布式系统:Broker、Producer和Consumer都原生自动支持分布式,自动实现负载均衡;
- 支持同步和异步复制两种高可用机制;
- 支持数据批量发送和拉取;
- 零拷贝技术(zero-copy):减少IO操作步骤,提高系统吞吐量;
- 数据迁移、扩容对用户透明;
- 无需停机即可扩展机器;
- 其他特性:丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制;
- 优点
- 客户端语言丰富:支持Java、.Net、PHP、Ruby、Python、Go等多种语言;
- 高性能:单机写入TPS约在100万条/秒,消息大小10个字节;
- 提供完全分布式架构,并有replica机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积;
- 支持批量操作;
- 消费者采用Pull方式获取消息。消息有序,通过控制能够保证所有消息被消费且仅被消费一次;
- 有优秀的第三方KafkaWeb管理界面Kafka-Manager;
- 在日志领域比较成熟,被多家公司和多个开源项目使用。
- 缺点
- Kafka单机超过64个队列/分区时,Load时会发生明显的飙高现象。队列越多,负载越高,发送消息响应时间变长;
- 使用短轮询方式,实时性取决于轮询间隔时间;
- 消费失败不支持重试;
- 支持消息顺序,但是一台代理宕机后,就会产生消息乱序;
- 社区更新较慢。
| 名称/特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|---|
| 所属社区/公司 | Apache | Mozilla Public License | 阿里巴巴 | Apache |
| 成熟度 | 成熟 | 成熟 | 比较成熟 | 成熟 |
| 授权方式 | 开源 | 开源 | 开源 | 开源 |
| 开发语言 | Java | Erlang | Java | Scala&Java |
| 客户端支持语言 | Java、C、C++、Python、PHP、Perl、.net 等 | 官方支持Erlang, Java, Ruby等,社区产出多种语言API,几乎支持所有常用语言 | Java、C++ (不成熟) | 官方支持Java,开源社区有多语言版本,如PHP,Python, Go, C/C++, Ruby,_eJS等语言 |
| 协议支持 | OpenWire、 STOMP、 REST, XMPP、AMQP | 多协议支持:AMQP, XMPP, SMTP, STOMP | 白己定义的一 套(社区提供JMS--不成熟) | 官方支持Java,开源社区有多语言版本,如PHP,Python, Go, C/C++, Ruby,_eJS等语言 |
| 消息批量操作 | 支持 | 不支持 | 支持 | 支持 |
| 消息推拉模式 | 多协议,Pull/Push均有支持 | 多协议,Pull/Push均有支持 | 多协议,Pull/Push均有支持 | Pull |
| 双机集群系统 | 基于ZooKeeper+ LevelDB的Master-Slave实现方式 | master/slave模式,master供服务,slave仅作备份 | 支持多Master模式、多Master多 Slave模式,异步 复制模式、多 Master 多 Slave 模式,同步双写 | 支持replica机制,leader宕掉后,备份自动顶替,并重新选举leader(基于 Zookeeper) |
| 数据可靠性 | master/slave(有较低的概率丢失数据) | 可以保证数据不丢,有slave用作备份 | 经过参数优化配置,可以做到0丢失 支持异步实时刷新,同步刷盘,同步复制,异步复制 | 经过参数优化配置,消息可以做到0丢失 数据可靠,并且有replica机制,有容错容灾能力 |
| 单机吞吐量 | 最差(万级) | 其次(万级) | 最高(十万级) | 次之(十万级) |
| topic数量对吞吐量的影响 | topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降,topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降 | topic从几十个到几百个的时候,吞吐量会大幅度下降,所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源 | ||
| 消息延迟 | 毫秒ms级 | 微秒μs级(最低) | 毫秒ms级(比kafka快) | 毫秒ms级 |
| 可用性 | 高,基于主从架构实现高可用性 | 高,基于主从架构实现高可用性 | 非常高,分布式架构 | 非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
| 持久化能力 | 内存、文件、数据库 | 内存、文件,支持数据堆积,但数据堆积反过来影响生产速率 | 磁盘文件 | 磁盘文件,只要磁盘容量够,可以做到无限消息堆积 |
| 是否有序 | 可以支持有序 | 若想有序,只能使用一个Client | 有序 | 多Client保证有序 |
| 事务支持 | 支持 | 不支持 | 支持 | 不支持,但可以通过LOW Level AP保证仅消费一次 |
| 集群 | 支持 | 支持 | 支持 | 支持 |
| 负载均衡 | 支持 | 支持 | 支持 | 支持 |
| 管理界面 | 一般 | 较好 | 命令行界面(没有图形化界面) | 官方只提供了命令行版,Yahoo幵源自己的Kafka Web管理界面Kafka-Manager |
| 部署方式 | 独立 | 独立 | 独立 | 独立 |
| 使用场景 | 电商、金融 等对事务一致性要求很高的场景 | 性能要求高的场景(主要适用于日志收集,数据采集 | ||
| 优势总结 | 1.非常成熟,功能强大,在业内大量的公司以及项目中都有应用 | 1.erlang语言开发,性能极其好,延时很低; 2.吞吐量到万级,MQ功能比较完备 3.开源提供的管理界面非常棒,用起来很好用 4.社区相对比较活跃,几乎每个月都发布几个版本分 5.在国内一些互联网公司近几年用rabbitmq也比较多一些 | 1.接口简单易用,而且毕竟在阿里大规模应用过,有阿里品牌保障 2.日处理消息上百亿之多,可以做到大规模吞吐,性能也非常好,分布式扩展也很方便,社区维护还可以,可靠性和可用性都是ok的,还可以支撑大规模的topic数量,支持复杂MQ业务场景 3.阿里出品都是java系的,我们可以自己阅读源码,定制自己公司的MQ,可以掌控 | 1.仅提供较少的核心功能,但是提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展 2.kafka最好是支撑较少的topic数量即可,保证其超高吞吐量 |
| 劣势总结 | 1.偶尔会有较低概率丢失消息 2.现在社区以及国内应用都越来越少,官方社区现在对ActiveMQ 5.x维护越来越少,几个月才发布一个版本 3.ActiveMQ主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用 | 1.RabbitMQ确实吞吐量会低一些,这是因为他做的实现机制比较重。 2.erlang开发,国内有几个公司有实力做erlang源码级别的研究和定制?如果说你没这个实力的话,确实偶尔会有一些问题,你很难去看懂源码,你公司对这个东西的掌控很弱,基本职能依赖于开源社区的快速维护和修复bug。 3.rabbitmq集群动态扩展会很麻烦,不过这个我觉得还好。其实主要是erlang语言本身带来的问题。很难读源码,很难定制和掌控。 | 1.社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准JMS规范走的有些系统要迁移需要修改大量代码 2.阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ挺好的 | 1.kafka唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集 |
模式
消息中间件的模式是指,消息的发送和接收模式。大致分为两种:
- 点对点模式
- 发布订阅模式
点对点模式
在点对点模型中,即使许多消息接收者在同一个消息队列中侦听,消息从消息发送者发送到只有一个接收者。在点对点模型中,通常使用术语消息发送者和消息接收者,而不是消息发布者和消息消费者。图示如下。
有两种类型的点对点消息传递:
- 即发即弃(单向)
- 在即发即弃中,消息发送方不会等待来自消息队列的任何响应。它不关心消息队列是否收到消息。在这个模型中,发起者和接收者根本没有交互。
- 消息传递和请求/回复(请求-响应)消息传递。
- 与即发即弃模式不同,在请求/回复消息模型中,消息发送方在一个队列上发送一条消息,然后等待接收方的响应。使用这种模型,发送关心它是否收到或尚未收到的消息状态。
发布订阅模式
发布/订阅消息传递或通常称为发布/订阅消息传递是一种异步形式。在该域中,消息生产者称为发布者,消息消费者称为订阅者。发布者向某个主题生成消息,然后订阅该主题的所有订阅者都将收到发送消息并消费它们。点对点和发布/订阅消息模型之间的区别在于消息的接收者数量。另一个区别是在点对点模型中,消息发送者必须知道接收者,但在发布/订阅中,消息发布者不需要知道消息将在哪里消费。该特性在应用发布/订阅消息模型时为应用程序提供了高度解耦。