消息中间件(MQ)介绍
# 1. 消息中间件(MQ)
# 1.1 消息队列回顾
消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构
目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ,消息中间件到底该如何使用,何时使用这是一个问题,胡乱地使用消息中间件增加了系统的复杂度,如果用不好消息中间件还不如不用。
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成,通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信,对于消息中间件,常见的角色大致也就有Producer(生产者)
、Consumer(消费者)
。
# 1.2 消息队列特点有哪些?
# 1.2.1 先进先出
先进先出是队列的一个特性
不能先进先出,都不能说是队列了,消息队列的顺序在入队的时候就基本已经确定了,一般是不需人工干预的,而且,最重要的是,数据是只有一条数据在使用中, 这也是MQ在诸多场景被使用的原因。
# 1.2.2 订阅
一般是MQ的广播模式,类似于java的观察者模式
发布订阅是一种很高效的处理方式,如果不发生阻塞,基本可以当做是同步操作,这种处理方式能非常有效的提升服务器利用率,这样的应用场景非常广泛。
# 1.2.3 持久化
持久化确保MQ的使用不只是一个部分场景的辅助工具,而是让MQ能像数据库一样存储核心的数据。
# 1.2.4 分布式
在现在大流量、大数据的使用场景下,只支持单体应用的服务器软件基本是无法使用的,支持分布式的部署,才能被广泛使用,而且,MQ的定位就是一个高性能的中间件。
# 1.3 消息队列通讯模式是什么?
# 1.3.1 点对点通讯点
点对点方式是最为传统和常见的通讯方式,它支持一对一、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。
# 1.3.2 发布/订阅(Publish/Subscribe)模式
发布/订阅功能使消息的分发可以突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序可以根据主题或内容接收到所需要的消息
发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者不必关心接收者的目的地址,而接收者也不必关心消息的发送地址,而只是根据消息的主题进行消息的收发,在MQ家族产品中,MQEventBroker是专门用于使用发布/订阅技术进行数据通讯的产品,它支持基于队列和直接基于TCP/IP两种方式的发布和订阅。
# 1.3.3 群集(Cluster)
为了简化点对点通讯模式中的系统配置,MQ提供Cluster(群集)的解决方案
群集类似于一个域(Domain),群集内部的队列管理器之间通讯时,不需要两两之间建立消息通道,而是采用群集(Cluster)通道与其它成员通讯,从而大大简化了系统配置
此外,群集中的队列管理器之间能够自动进行负载均衡,当某一队列管理器出现故障时,其它队列管理器可以接管它的工作,从而大大提高系统的高可靠性。
# 1.4 为什么使用消息队列?
其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么?
先说一下消息队列常见的使用场景吧,其实场景有很多,但是比较核心的有 3 个:解耦、异步、削峰。
# 1.4.1 应用解耦
在快递柜业务流程中,快递员投柜后需要经历扣减系统费、短信通知用户和推送通知快递公司三个业务动作
传统做法需要依次执行这些业务东西,如果其中某一步异常(例如用户手机未开机或者快递公司接口故障),将会延迟甚至中断整个投柜流程,严重影响用户体验。
如果接口层收到投柜数据后,写入消息到MQ,后续三个子系统各自消费处理,将可以完美解决该问题,并且子系统故障不影响上游系统!此为 解耦!
# 1.4.2 异步
比如快递投柜后,用户马上就结束了,不会等待到发送短信或者通知快递公司结束的,直接将消息投递到MQ,然后就直接结束,具体到扣减系统费以及后续的通知,都是异步操作的,不需要用户关心的,这就是将用户的同步操作转换为异步操作。
如果全部同步操作需要15S,而发送到MQ后交给系统异步处理用户只需要1S就可以完成操作。
# 1.4.3 流量削峰
就像用户投递快递,高峰到40W每秒,但是我们的后续处理业务每秒只能20W,还剩下20W在MQ进行堆积,这就是MQ很重要的流量削峰的能力,将用户的洪峰流量,让后台慢慢来处理,MQ承担一个缓冲的作用
就像这个波形图一样,如果用户请求的并发量的最高峰时40W,系统的承载能力只能达到30W,就可以使用MQ进行削峰,将系统最高40W的并发削峰为最高只有20万,就是时间换空间的做法。
秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。
- 可以控制活动人数,超过此一定阀值的订单直接丢弃
- 可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单)
- 用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面.
- 秒杀业务根据消息队列中的请求信息,再做后续处理.
# 1.4 消息队列有什么缺点?
优点上面已经说了,就是在特殊场景下有其对应的好处,解耦、异步、削峰。
# 1.4.1 系统可用性降低
消息队列在系统中充当一个中间人的身份,如果该中间人突然失联了,那其他两方就不知所措了,最后也就导致系统之间无法互动。
就像我们投递快递,如果MQ出现问题,那麽我们整个系统调用链路就会断开,前后端将无法通讯
# 1.4.2 系统复杂性提高
在使用消息队列的过程中,难免会出现生产者、MQ、消费者宕机不可用的情况,那么随之带来的问题就是消息重复、消息乱序、消息堆积等等问题都需要我们来控制。
# 1.4.3 一致性问题
如下图所示,系统需要保证快递投递,扣减系统费,通知等之间的数据一致性,如果系统短信通知,快递通知执行成功,扣减系统费执行失败时,就会出现数据不一致问题
如果出现这种问题,我们需要有应对方案,比如重试等方案。
所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了 10 倍。但是关键时刻,用,还是得用的。
# 2. 常用消息队列有哪些?
消息队列是分布式应用间交换信息的重要组件,消息队列可驻留在内存或磁盘上, 队列可以存储消息直到它们被应用程消费。
通过消息队列,应用程序可以在不知道彼此位置的情况下独立处理消息,或者在处理消息前不需要等待接收此消息。
所以消息队列可以解决应用解耦、异步消息、流量削锋等问题,是实现高性能、高可用、可伸缩和最终一致性架构中不可以或缺的一环。
现在比较常见的消息队列产品主要有ActiveMQ、RabbitMQ、Kafka、RocketMQ等
# 2.1 ActiveMQ
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线,ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
# 2.1.1 特性
- 多种语言和协议编写客户端。语言: Java,C,C++,C#,Ruby,Perl,Python,PHP。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP
- 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)
- 对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性
- 通过了常见J2EE服务器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上
- 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
- 支持通过JDBC和journal提供高速的消息持久化
- 从设计上保证了高性能的集群,客户端-服务器,点对点
- 支持Ajax
- 支持与Axis的整合
- 可以很容易得调用内嵌JMS provider,进行测试
# 2.1.2 使用建议
一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了;
# 2.2 RabbitMQ
RabbitMQ是流行的开源消息队列系统,用erlang语言开发,RabbitMQ是AMQP(高级消息队列协议)的标准实现。
支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX,持久化,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗
# 2.2.1 特性
- erlang语言开发,性能极其好,延时很低
- 吞吐量到万级,MQ功能比较完备
- 开源提供的管理界面非常棒,用起来很好用
- 社区相对比较活跃,几乎每个月都发布几个版本分
- 在国内一些互联网公司近几年用RabbitMQ也比较多一些
# 2.2.2 使用建议
大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高,所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择,对自家技术没有过高自信的话,可以使用RabbitMQ,人家有活跃的开源社区,绝对不会黄。
# 2.3 Kafka
Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。
这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素, 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决,对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,也是为了通过集群机来提供实时的消费。
# 2.3.1 特性
- 通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。(文件追加的方式写入数据,过期的数据定期删除)
- 高吞吐量:即使是非常普通的硬件Kafka也可以支持每秒数百万的消息
- 支持通过Kafka服务器和消费机集群来分区消息
- 支持Hadoop并行数据加载
# 2.3.2 使用建议
如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。
# 2.3.3 应用场景
一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)要求稍低的场景使用。
# 2.3.3.1 日志收集
一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer;
# 2.3.3.2 消息系统
解耦生产者和消费者、缓存消息等;
# 2.3.3.3 用户活动跟踪
kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后消费者通过订阅这些topic来做实时的监控分析,亦可保存到数据库;
# 2.3.3.4 运营指标
kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告;
# 2.3.3.5 流式处理
比如spark streaming和storm。
# 2.4 RocketMQ
RocketMQ是阿里开源的消息中间件,纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。
RocketMQ思路起源于Kafka,但并不是简单的复制,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景,支撑了阿里多次双十一活动
因为是阿里内部从实践到产品的产物,因此里面很多接口、api并不是很普遍适用,可靠性毋庸置疑,而且与Kafka一脉相承(甚至更优),性能强劲,支持海量堆积。
# 2.4.1 特点
- 接口简单易用,源码是阿里出品,可自定义MQ
- topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降
- 消息可用性极高,经过参数优化配置,可以做到0丢失
- MQ功能较为完善,还是分布式的,扩展性好
# 2.4.2 使用建议
现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 Apache,但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。
# 3. 怎样选型MQ?
到底应该哪个方案,还是要看具体的需求,在我们的设计中,MQ的功能与业务无关,因此优先考虑使用已有的中间件搭建,那么具本选择哪个中间件呢?
# 3.1 需求分析
# 3.1.1 功能需求
除了最基本生产消费模型,还需要MQ能支持REQUEST-REPLY模型,以提供对同步调用的支持此外,如果MQ能提供PUBLISH-SUBSCRIBE模型,则事件代理的实现可以更加简单。
# 3.1.2 性能需求
考虑未来一到两年内产品的发展,消息队列的呑吐量预计不会超过 1W qps,但由单条消息延迟要求较高,希望尽量的短。
# 3.1.3 可用性需求
因为是在线服务,因此需要较高的可用性,但充许有少量消息丢失。
# 3.1.4 易用性需求
包括学习成本、初期的开发部署成本、日常的运维成本等。
# 3.2 横向对比
特性 | ActiveMQ | RabbitMQ | Kafka | RocketMQ |
---|---|---|---|---|
PRODUCER-COMSUMER | 支持 | 支持 | 支持 | 支持 |
PUBLISH-SUBSCRIBE | 支持 | 支持 | 支持 | 支持 |
REQUEST-REPLY | 支持 | 支持 | - | 支持 |
API完备性 | 高 | 高 | 高 | 低(静态配置) |
多语言支持 | 支持,JAVA优先 | 语言无关 | 支持,JAVA优先 | 支持 |
单机呑吐量 | 万级 | 万级 | 十万级 | 单机万级 |
消息延迟 | 毫秒级 | 微秒级 | 毫秒级 | 毫秒级 |
可用性 | 高(主从) | 高(主从) | 很高(分布式) | 非常高(分布式) |
消息丢失 | - | 低 | 理论上不会丢失 | 理论上不会丢失 |
消息重复 | - | 可控制 | 理论上会有重复 | 允许重复 |
文档的完备性 | 高 | 高 | 高 | 中 |
提供快速入门 | 有 | 有 | 有 | 无 |
首次部署难度 | - | 低 | 中 | 高 |
注: - 表示尚未查找到准确数据
# 3.2.1 个人建议
- 中小型公司,技术一般,可以考虑用 RabbitMQ;
- 大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择
- 实时计算、日志采集:使用 kafka;
# 3.3 如何选型?
MQ | 描述 |
---|---|
RabbitMQ | erlang开发,对消息堆积的支持并不好,当大量消息积压的时候,会导致 RabbitMQ 的性能急剧下降,每秒钟可以处理几万到十几万条消息。 |
RocketMQ | java开发,面向互联网集群化功能丰富,对在线业务的响应时延做了很多的优化,大多数情况下可以做到毫秒级的响应,每秒钟大概能处理几十万条消息。 |
Kafka | Scala开发,面向日志功能丰富,性能最高,当你的业务场景中,每秒钟消息数量没有那么多的时候,Kafka 的时延反而会比较高,所以,Kafka 不太适合在线业务场景。 |
ActiveMQ | java开发,简单,稳定,性能不如前面三个,小型系统用也ok,但是不推荐,推荐用互联网主流的。 |