常用消息中间件选型及使用场景分析

2021/2/19 mq

# 消息中间件简介

# 常用概念

# 消息

消息是指软件对象之间进行交互作用和通讯利用的一种方式。

image-20210219092840842

# 中间件

非底层操作系统软件,非业务应用软件,不是直接给最终用户使用的,不能直接给客户带来价值的软件统称为中间件

# 消息队列

消息队列是消息中间件的一种实现方式。

image-20210219093024469

# 消息中间件

消息中间件是基于队列与消息传递技术,在网络环境中为应用系统提供同步或异步、可靠的消息传输的支撑性软件系统。

关注于数据的发送和接受,利用高效可靠的异步消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。

image-20210219093227702

这可能比较抽象,但消息队列是消息中间件的一种实现方式,后文我们将举出实际的场景来理解消息中间件。

# 消息中间件产生背景

==在客户端与服务器进行通讯时.客户端调用后,必须等待服务对象完成处理返回结果才能继续执行。==

这样会引发很多的问题:

  • 客户与服务器对象的生命周期紧密耦合,客户进程和服务对象进程都都必须正常运行;

  • 如果由于服务对象崩溃或者网络故障导致用户的请求不可达,客户会受到异常

为了解决这样的问题,消息中间件技术应运而生。

面向消息的中间件(MessageOrlented MiddlewareMOM)较好的解决了以上问题。发送者将消息发送给消息服务器,消息服务器将消感存放在若千队列中,在合适的时候再将消息转发给接收者。

这种模式下,好处有很多:

  1. 发送和接收是异步的,发送者无需等待;

  2. 二者的生命周期未必相同: 发送消息的时候接收者不一定运行,接收消息的时候发送者也不一定运行;

  3. 一对多通信: 对于一个消息可以有多个接收者。

# 消息队列的特点

  • 先进先出:消息队列的顺序在入队的时候就基本已经确定了,一般是不需人工干预的。

  • 发布订阅:发布订阅是一种很高效的处理方式,如果不发生阻塞,基本可以当成是同步操作。

  • 持久化:持久化确保消息队列的使用不只是一个部分场景的辅助工具,而是让消息队列能像数据库一样存储核心的数据。

  • 分布式:在现在大流量、大数据的使用场景下,支持分布式的部署,才能被广泛使用。消息队列的定位就是一个高性能的中间件。

# 消息中间件常用协议

# 1) AMQP协议(Advanced Message Queuing Protocol)

AMQP是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。

优点:可靠、通用

# 2) MQTT协议(Message Queuing Telemetry Transport,消息队列遥测传输)

MQTT是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。

优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统

# 3) STOMP协议(Streaming Text Orientated Message Protocol)

STOMP是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。

优点:命令模式(非topic\queue模式)

# 4) XMPP协议(Extensible Messaging and Presence Protocol,可扩展消息处理现场协议)

XMPP是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。

优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大

# 5) 其他基于TCP/IP自定义的协议

有些特殊框架(如:redis、kafka、zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCP\IP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能。

# 扩展:JMS

# 1.什么是JMS

JMS即Java消息服务 (opens new window)(Java Message Service)应用程序接口,是一个Java平台 (opens new window)中关于面向消息中间件 (opens new window)(MOM)的API (opens new window),用于在两个应用程序之间,或分布式系统 (opens new window)中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持(百度百科给出的概述)。我们可以简单的理解:两个应用程序之间需要进行通信,我们使用一个JMS服务,进行中间的转发,通过JMS 的使用,我们可以解除两个程序之间的耦合。

# 2.JMS的优势

①Asynchronous(异步)

JMS is asynchronous by default. So to receive a message, the client is not required to send the request. The message will arrive automatically to the client as they become available.(JMS 原本就是一个异步的消息服务,客户端获取消息的时候,不需要主动发送请求,消息会自动发送给可用的客户端)

②Reliable(可靠)

JMS provides the facility of assurance that the message will delivered once and only once. You know that duplicate messages create problems. JMS helps you avoiding such problems.(JMS保证消息只会递送一次。大家都遇到过重复创建消息问题,而JMS能帮你避免该问题。)

# 3. JMS的消息模型

JMS具有两种通信模式:

①Point-to-Point Messaging Domain (点对点)

②Publish/Subscribe Messaging Domain (发布/订阅模式)

​ 在JMS API出现之前,大部分产品使用“点对点”和“发布/订阅”中的任一方式来进行消息通讯。JMS定义了这两种消息发送模型的规范,它们相互独立。任何JMS的提供者可以实现其中的一种或两种模型,这是它们自己的选择。JMS规范提供了通用接口保证我们基于JMS API编写的程序适用于任何一种模型。

# (1)Point-to-Point Messaging Domain(点对点通信模型)

image-20210219142549770

在点对点通信模式中,应用程序由消息队列,发送方,接收方组成。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

特点:

  • 每个消息只要一个消费者
  • 发送者和接收者在时间上是没有时间的约束,也就是说发送者在发送完消息之后,不管接收者有没有接受消息,都不会影响发送方发送消息到消息队列中。
  • 发送方不管是否在发送消息,接收方都可以从消息队列中取到消息(The receiver can fetch message whether it is running or not when the sender sends the message)
  • 接收方在接收完消息之后,需要向消息队列应答成功
# (2)Publish/Subscribe Messaging Domain(发布/订阅通信模型)

image-20210219142941809

在发布/订阅消息模型中,发布者发布一个消息,该消息通过topic传递给所有的客户端。该模式下,发布者与订阅者都是匿名的,即发布者与订阅者都不知道对方是谁。并且可以动态的发布与订阅Topic。Topic主要用于保存和传递消息,且会一直保存消息直到消息被传递给客户端。

特点:

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

# 4.JMS接收消息的方式

在JMS中,消息的产生和消息的消费是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。

(1)同步(Synchronous)

在同步消费信息模式模式中,订阅者/接收方通过调用 receive()方法来接收消息。在receive()方法中,线程会阻塞直到消息到达或者到指定时间后消息仍未到达。

(2)异步(Asynchronous)

使用异步方式接收消息的话,消息订阅者需注册一个消息监听者,类似于事件监听器,只要消息到达,JMS服务提供者会通过调用监听器的onMessage()递送消息。

# 5.JMS与AMQP的区别

  • JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
  • JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
  • JMS规定了两种消息模型 点对点、发布订阅;而AMQP的消息模型有7种

# 为什么要使用消息中间件?

以一个电商平台为例,分析为什么要使用消息中间件,以便于理解消息中间件的使用场景。

订单系统堪称是整个电商交易平台的核心,它需要与很多内部模块、外部第三方系统打交道。从下订单到支付完成这个过程中,需要完成很多额外的步骤:

  • 为用户积分
  • 发放红包卡券
  • 库存扣减
  • 通知物流系统
  • 发送短信通知
  • ...

订单系统流程图

image-20210219145635743

订单系统存在的问题

对于一个电商APP而言,每卖掉了一个商品,就要扣减掉商品的库存,而且一旦用户成功支付了,还需要将订单的状态更新成待发货。

在完成这些最核心的功能后,其实是有很多事情要做的,比如上图红色的部分。如果这些动作都以同步方式来完成,根据线上系统的一般统计,多个子步骤全部执行完毕,加起来大概需要1秒~2秒的时间。有时候在高峰期并发量特别大,服务器的磁盘、IO、CPU的负载会很高,执行SQL语句的性能也会有所下降。因此有的时候甚至需要几秒钟的时间完成上述几个步骤。

那么影响是什么呢? 想象一下,如果你是一个用户,在支付完一个订单之后,界面上会有一个圈圈不停的旋转,让你等待好几秒之后才能提示支付成功。对用户来说几秒钟的时间,会让人非常不耐烦的!

所以首先针对==子步骤过多、速度过慢、让用户支付之后等待时间过长==的问题,就是订单系统亟需解决的问题! 而解决这个问题的一大利器就是消息中间件,英文全称“Message Queue”,简称MQ。

引入MQ对订单系统的好处

image-20210219150123241

在引入消息中间件以后,系统A和系统B之间就由同步变为异步通信,而完成这样的一个核心概念就是“消息”

系统A发送消息给MQ后,就认为已经完成了自己的任务;然后系统B根据自己的情况,可能会在系统A投递消息到MQ之后的1秒内,也可能是1分钟之后,也可能是1小时之后,多长时间都有可能。

反正不管是多长时间后,系统B会根据自己的节奏从MQ里获取到一条属于自己的消息,再根据消息的指示完成自己的工作。 在“异步调用”的整个过程中,系统A仅仅是发个消息到MQ,至于系统B什么时候获取消息,有没有获取消息,系统A是不管的。

对于订单系统而言,在引入MQ后,我们可以让订单系统仅仅完成最核心的功能,然后发送消息到MQ。比如需要进行减库存,就发送一个消息到库存消息队列中,然后库存系统从这个MQ里获取消息再进行处理就可以,把这些很耗时的步骤慢慢执行,从而也实现了系统之间的解耦。

在双11大促活动的时候,同样可以让瞬间涌入的大量下单请求到MQ里去排队,然后让订单系统在后台慢慢的获取订单,以数据库可以接受的速率完成操作,避免瞬间请求量过大击垮数据库。

# 消息中间件的使用场景

# 1)异步处理

场景说明:提供新用户注册后,发送短信或邮件的功能

# 传统方式

传统的做法有有两种 1.串行方式;2.并行方式

# 1.串行方式

将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。

image-20210219151148721

# 2.并行方式

将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。

image-20210219151135029

# 分析

假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。

如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。

# 消息中间件方式

引入消息队列,将不是必须的业务逻辑,将异步处理改造后的架构如下:

image-20210219151503802

# 分析

按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。至于从消息队列中发送邮件和短信完全可以异步处理,不受时间的限制,等邮件服务和短信服务需要的时候从消息队列中消费消息即可。

因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。

# 2)应用解耦

场景说明:用户下单后,订单系统需要通知库存系统。

# 传统方式

传统的做法是,订单系统直接调用库存系统的接口。

image-20210219152022454

传统模式的缺点:假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,订单系统与库存系统耦合

# 消息中间件方式

引入应用消息队列后的方案:

image-20210219152130403

订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作

假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦

# 3)流量削峰

场景说明:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。通过加入消息队列完成如下功能:

  • 可以控制活动的人数

  • 可以缓解短时间内高流量压垮应用

image-20210219152635394

用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。秒杀业务根据消息队列中的请求信息,再做后续处理。

# 其他应用场景

# 1.解决数据丢失问题

有些情况下,处理数据的过程会失败。除非数据被持久化(==但又不想给数据库过大的压力==),否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。

# 2.过载保护

在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提取预知;如果为了能处理这类瞬间峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。

# 3.异常恢复

系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

# 4.缓冲

在任何重要的系统中,都会有需要不同的处理时间的元素。消息队列通过一个缓冲层来帮助任务最高效率的执行,该缓冲有助于控制和优化数据流经过系统的速度。以调节系统响应时间。

# 5. 数据流处理

分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择。

# 常用消息中间件介绍

当前使用较多的消息队列有RabbitMQRocketMQActiveMQKafka、ZeroMQ、MetaMQ等,而部分数据库如Redis、MySQL以及phxsql也可实现消息队列的功能。

# 1)RabbitMQ

RabbitMQ于2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。

RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。

# 主要特性

  1. 可靠性:提供了多种技术可以让你在性能和可靠性之间进行权衡。这些技术包括持久性机制、投递确认、发布者证实和高可用性机制;
  2. 灵活的路由:消息在到达队列前是通过交换机进行路由的。RabbitMQ为典型的路由逻辑提供了多种内置交换机类型。如果你有更复杂的路由需求,可以将这些交换机组合起来使用,你甚至可以实现自己的交换机类型,并且当做RabbitMQ的插件来使用;
  3. 消息集群:在相同局域网中的多个RabbitMQ服务器可以聚合在一起,作为一个独立的逻辑代理来使用;
  4. 队列高可用:队列可以在集群中的机器上进行镜像,以确保在硬件问题下还保证消息安全;
  5. 支持多种协议:支持多种消息队列协议;
  6. 支持多种语言:用Erlang语言编写,支持只要是你能想到的几乎所有编程语言;
  7. 管理界面:RabbitMQ有一个易用的用户界面,使得用户可以监控和管理消息Broker的许多方面;
  8. 跟踪机制:如果消息异常,RabbitMQ 提供消息跟踪机制,使用者可以找出发生了什么;
  9. 插件机制:提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。

# 优点

  1. 由于Erlang语言的特性,消息队列性能较好,支持高并发;
  2. 健壮、稳定、易用、跨平台、支持多种语言、文档齐全;
  3. 有消息确认机制和持久化机制,可靠性高;
  4. 高度可定制的路由;
  5. 管理界面较丰富,在互联网公司也有较大规模的应用,社区活跃度高。

# 缺点

  1. 尽管结合 Erlang 语言本身的并发优势,性能较好,但是不利于做二次开发和维护;
  2. 实现了代理架构,意味着消息在发送到客户端之前可以在中央节点上排队。此特性使得RabbitMQ易于使用和部署,但是使得其运行速度较慢,因为中央节点 增加了延迟,消息封装后也比较大;需要学习比较复杂的接口和协议,学习和维护成本较高。

# 2)Kafka

Apache Kafka是LinkedIn开源的分布式发布-订阅消息系统(使用scala实现的一个高性能分布式Publish/Subscribe消息队列系统)。它最初由LinkedIn公司基于独特的设计实现为一个分布式的日志提交系统(a distributed commit log),之后成为Apache项目的一部分,目前归属于Apache顶级项目。

Kafka主要为高吞吐量的订阅发布系统而设计,追求速度与持久化。kafka中的消息由键、值、时间戳组成,kafka不记录每个消息被谁使用,只通过偏移量记录哪些消息是未读的,kafka中可以指定消费组来实现订阅发布的功能。

# 主要特性

  1. 快速持久化:可以在O(1)的系统开销下进行消息持久化;
  2. 高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
  3. 高堆积:支持topic下消费者较长时间离线,消息堆积量大;
  4. 完全的分布式系统:Broker、Producer和Consumer都原生自动支持分布式,自动实现负载均衡;
  5. 支持同步和异步复制两种高可用机制;
  6. 支持数据批量发送和拉取;
  7. 零拷贝技术(zero-copy):减少IO操作步骤,提高系统吞吐量;
  8. 数据迁移、扩容对用户透明;
  9. 无需停机即可扩展机器;
  10. 支持Hadoop数据并行加载:对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。
  11. 其他特性:丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制;

# 优点

  1. 客户端语言丰富:支持Java、.Net、PHP、Ruby、Python、Go等多种语言;
  2. 高性能:单机写入TPS约在100万条/秒,消息大小10个字节;
  3. 提供完全分布式架构,并有replica机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积;
  4. 支持批量操作;
  5. 消费者采用Pull方式获取消息。消息有序,通过控制能够保证所有消息被消费且仅被消费一次;
  6. 有优秀的第三方KafkaWeb管理界面Kafka-Manager;
  7. 在日志领域比较成熟,被多家公司和多个开源项目使用。

# 缺点

  1. Kafka单机超过64个队列/分区时,Load时会发生明显的飙高现象。队列越多,负载越高,发送消息响应时间变长;
  2. 使用短轮询方式,实时性取决于轮询间隔时间;
  3. 消费失败不支持重试;
  4. 支持消息顺序,但是一台代理宕机后,就会产生消息乱序;
  5. 社区更新较慢。

# 3)RocketMQ

阿里系下开源的一款分布式、队列模型的消息中间件,原名Metaq,3.0版本名称改为RocketMQ,是阿里参照kafka设计思想使用java实现的一套mq。同时将阿里系内部多款mq产品(Notify、metaq)进行整合,只维护核心功能,去除了所有其他运行时依赖,保证核心功能最简化,在此基础上配合阿里上述其他开源产品实现不同场景下mq的架构,在阿里内部被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。

支持的客户端语言不多,目前是Java及C++,其中C++还不成熟;

# 主要特性

  1. 基于 队列模型:具有高性能、高可靠、高实时、分布式等特点;
  2. Producer、Consumer、队列都支持分布式;
  3. Producer向一些队列轮流发送消息,队列集合称为Topic。Consumer如果做广播消费,则一个Consumer实例消费这个Topic对应的所有队列;如果做集群消费,则多个Consumer 实例平均消费这个Topic对应的队列集合;
  4. 能够保证严格的消息顺序;
  5. 提供丰富的消息拉取模式;
  6. 高效的订阅者水平扩展能力;
  7. 实时的消息订阅机制;
  8. 亿级消息堆积 能力;
  9. 较少的外部依赖。

# 优点

  1. 单机支持1万以上持久化队列;
  2. RocketMQ的所有消息都是持久化的,先写入系统PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据,而访问时,直接从内存读取。
  3. 模型简单,接口易用(JMS的接口很多场合并不太实用);
  4. 性能非常好,可以允许大量堆积消息在Broker中;
  5. 支持多种消费模式,包括集群消费、广播消费等;
  6. 各个环节分布式扩展设计,支持主从和高可用;
  7. 开发度较活跃,版本更新很快。

# 缺点

  1. 支持的 客户端语言不多,目前是Java及C++,其中C++还不成熟;
  2. RocketMQ社区关注度及成熟度也不及前两者;
  3. 没有Web管理界面,提供了一个 CLI (命令行界面) 管理工具带来查询、管理和诊断各种问题;
  4. 没有在MQ核心里实现JMS等接口;

# 4)ActiveMQ

ActiveMQ是由Apache出品,ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。

# 主要特性

  1. 服从JMS规范:JMS 规范提供了良好的标准和保证,包括:同步 或 异步 的消息分发,一次和仅一次的消息分发,消息接收和订阅等等。遵从JMS规范的好处在于,不论使用什么JMS实现提供者,这些基础特性都是可用的;
  2. 连接灵活性:ActiveMQ提供了广泛的连接协议,支持的协议有:HTTP/S,IP多播,SSL,TCP,UDP等等。对众多协议的支持让ActiveMQ拥有了很好的灵活性;
  3. 支持的协议种类多:OpenWire、STOMP、REST、XMPP、AMQP;
  4. 持久化插件和安全插件:ActiveMQ提供了多种持久化选择。而且,ActiveMQ的安全性也可以完全依据用户需求进行自定义鉴权和授权;
  5. 支持的客户端语言种类多:除了Java之外,还有:C/C++,.NET,Perl,PHP,Python,Ruby;
  6. 代理集群:多个ActiveMQ代理可以组成一个集群来提供服务;
  7. 异常简单的管理:ActiveMQ是以开发者思维被设计的。所以,它并不需要专门的管理员,因为它提供了简单又使用的管理特性。有很多中方法可以监控ActiveMQ不同层面的数据,包括使用在JConsole或者在ActiveMQ的WebConsole中使用JMX。通过处理JMX的告警消息,通过使用命令行脚本,甚至可以通过监控各种类型的日志。

# 优点

  1. 跨平台(JAVA编写与平台无关,ActiveMQ几乎可以运行在任何的JVM上);
  2. 可以用JDBC:可以将数据持久化到数据库。虽然使用JDBC会降低ActiveMQ的性能,但是数据库一直都是开发人员最熟悉的存储介质;
  3. 支持JMS规范:支持JMS规范提供的统一接口;
  4. 支持自动重连和错误重试机制;
  5. 有安全机制:支持基于shiro,jaas等多种安全配置机制,可以对Queue/Topic进行认证和授权;
  6. 监控完善:拥有完善的监控,包括WebConsole,JMX,Shell命令行,Jolokia的RESTful API;
  7. 界面友善:提供的WebConsole可以满足大部分情况,还有很多第三方的组件可以使用,比如hawtio;

# 缺点

  1. 社区活跃度不及RabbitMQ高;
  2. 根据其他用户反馈,会出莫名其妙的问题,会丢失消息;
  3. 目前重心放到activemq6.0产品Apollo,对5.x的维护较少;
  4. 不适合用于上千个队列的应用场景;

其余的消息中间件在目前并没有这四种常用,所以不做介绍和考虑。

# 常用消息中间件技术对比

RabbitMQ RocketMQ Kafka ActiveMQ
设计定位 可靠消息传输 非日志的可靠消息传输 实时数据处理及日志处理 可靠消息传输
所属社区/公司 Apache Alibaba开发,现已加入Apache Apache Apache
成熟度 成熟 成熟 日志领域成熟 成熟
社区活跃度
API完备性
开发语言 ErLang Java Scala Java
持久化方式 内存、文件 磁盘文件 磁盘文件 内存、文件、数据库
客户端支持语言 Python
Java
Ruby
PHP
C#
JavaScript
Go
Elixir
Objective-C
Swift
Java、C++(不成熟) C
C++
Erlang
Java
.net
per
PHP
Python
Ryby
Go
JavaScript
C
C++
C#
Delphi
Erlang
Adobe Flash
Haskell
Java
JavaScript
Perl
PHP
Pike
Pytho
Ruby
部署方式 单机/集群 单机/集群 单机/集群 单机/集群
集群管理方式 独立 nameserver zookeeper 独立
可用性 高,基于主从架构实现高可用 非常高,分布式架构 非常高,分布式
一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
高,基于主从架构实现高可用
消息写入性能 较好 很好 非常好 较好
单机队列数 依赖内存 单机最高5万 单机超过64个队列或分区时负载变高 较好
单机吞吐量 万级,比 RocketMQ、Kafka 低一个数量级 10 万级,高吞吐 10 万级,高吞吐 万级,比 RocketMQ、Kafka 低一个数量级
时效性 微秒级,这是 RabbitMQ 的一大特点,延迟最低 ms 级 延迟在 ms 级以内 ms 级
事务消息 不支持 支持 不支持 支持
消息过滤 不支持 支持 不支持 不支持
消息失败重试 支持 支持 不支持 支持
消息重新消费
(消息幂等性问题)
不支持 支持 支持 支持
批量发送 不支持 支持 支持 支持
消息清理 可用内存少于40%触发gc 指定文件保存时间过期删除 指定文件保存时间过期删除 指定文件保存时间过期删除

# 消息中间件如何选型?

# 消息中间件技术选型需要考虑的问题

  1. MQ的性能表现怎么样?
  2. 假如机器硬件条件相同,比如互联网公司最为常见的2G4核或者4G8核,能抗住多少QPS,每秒几千QPS或几万QPS,什么程度会到达性能瓶颈?
  3. 性能有多高,比如向MQ发送消息需要2毫秒还是20毫秒?
  4. 能够高可用吗,如果部署的一台服务器宕机了怎么办,有没有自动修复的机制?
  5. 可靠吗,会不会丢失数据?
  6. 支持线性的集群扩展吗,添加更多机器的机制复杂吗?
  7. 功能性满足吗,比如经常需要使用的功能:延迟消息、事务消息、消息累积、消息回溯、死信队列等等?
  8. 官方文档是否完整且清晰,社区活跃吗,如果遇到技术问题我们是否方便地找到解决办法?
  9. 在工业中是否已经被广泛使用了,已经被大公司验证过它的质量?
  10. 它是用什么语言写的,如果有个性化的需求,是否可以对源码进行修改?

# 消息中间件技术选型建议

# 1)综合对比

ActiveMQ

一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃。

RabbitMQ

后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;

RocketMQ

现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 Apache,但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,人家有活跃的开源社区,绝对不会黄。

所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。

Kafka

如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。

# 2)从公司基础建设力量角度

  1. 中小型软件公司,建议选RabbitMQ,一方面,erlang语言天生具备高并发的特性,而且他的管理界面用起来十分方便。他的弊端也在这里,虽然RabbitMQ是开源的,然而国内有几个能定制化开发erlang的程序员呢?所幸,RabbitMQ的社区十分活跃,可以解决开发过程中遇到的bug,这点对于中小型公司来说十分重要。不考虑RocketMQ和kafka的原因是,一方面中小型软件公司不如互联网公司,数据量没那么大,选消息中间件,应首选功能比较完备的,所以kafka排除。不考虑RocketMQ的原因是,RocketMQ是阿里出品,如果阿里放弃维护RocketMQ,中小型公司一般抽不出人来进行RocketMQ的定制化开发,因此不推荐。
  2. 大型软件公司,根据具体使用在RocketMQ和kafka之间二选一。一方面,大型软件公司,具备足够的资金搭建分布式环境,也具备足够大的数据量。针对RocketMQ,大型软件公司也可以抽出人手对RocketMQ进行定制化开发,毕竟国内有能力改JAVA源码的人,还是相当多的。至于kafka,根据业务场景选择,如果有日志采集功能,肯定是首选kafka了。

# 3)从业务场景角度出发

  1. RocketMQ定位于非日志的可靠消息传输(日志场景也OK),目前RocketMQ在阿里集团被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。
  2. Kafka是LinkedIn开源的分布式发布-订阅消息系统,目前归属于Apache定级项目。Kafka主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输。0.8版本开始支持复制,不支持事务,对消息的重复、丢失、错误没有严格要求,适合产生大量数据的互联网服务的数据收集业务。
  3. RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。

# 参考文献: