1、软件包
文章所需软件包下载
2、简介
“消息队列”是在消息的传输过程中保存消息的容器。“消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。

Producer:消息生产者,负责产生和发送消息到 Broker;
Broker:消息处理中心。负责消息存储、确认、重试等,一般其中会包含多个 queue;
Consumer:消息消费者,负责从 Broker 中获取消息,并进行相应处理;
现在常用的MQ组件有ActiveMQ、RabbitMQ、RocketMQ、ZeroMQ,当然近年来火热的Kafka,从某些场景来说,也是MQ,当然kafka的功能更加强大。
虽然不同的MQ都有自己的特点和优势,但是,不管是哪种MQ,都有MQ本身自带的一些特点,下面,咱们谈谈消息队列的的特点、优势、选型、以及应用场景。
3、为什么会需要消息队列(MQ)
主要原因是由于在高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达MySQL,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。通过使用消息队列,我们可以异步处理请求,从而缓解系统的压力。
美国计算机科学家,LaTex的作者Leslie Lamport说:“分布式系统就是这样一个系统,系统中一个你甚至都不知道的计算机出了故障,却可能导致你自己的计算机不可用。”一语道破了开发分布式系统的玄机,那就是它的复杂与不可控。所以Martin Fowler强调:分布式调用的第一原则就是不要分布式。这句话看似颇具哲理,然而就企业应用系统而言,只要整个系统在不停地演化,并有多个子系统共同存在时,这条原则就会被迫打破。因为在当今的企业应用系统中,很难寻找到完全不需要分布式调用的场景。Martin Fowler提出的这条原则,一方面是希望设计者能够审慎地对待分布式调用,另一方面却也是分布式系统自身存在的缺陷所致。无论是CORBA,还是EJB 2;无论是RPC平台,还是Web Service,都因为驻留在不同进程空间的分布式组件,而引入额外的复杂度,并可能对系统的效率、可靠性、可预测性等诸多方面带来负面的影响。
然而,不可否认的是在企业应用系统领域,我们总是会面对不同系统之间的通信、集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越重要,它在架构设计中就更加凸显其价值。并且,从业务分析与架构质量的角度来讲,我们也希望在系统架构中尽可能地形成对服务的重用,通过独立运行在进程中服务的形式,彻底解除客户端与服务端的耦合。这常常是架构演化的必然道路。在我的同事陈金洲发表在InfoQ上的文章《架构腐化之谜》中,就认为可以通过“将独立的模块放入独立的进程”来解决架构因为代码规模变大而腐化的问题。
随着网络基础设施的逐步成熟,从RPC进化到Web Service,并在业界开始普遍推行SOA,再到后来的RESTful平台以及云计算中的PaaS与SaaS概念的推广,分布式架构在企业应用中开始呈现出不同的风貌,然而殊途同归,这些分布式架构的目标仍然是希望回到建造巴别塔的时代,系统之间的交流不再为不同语言与平台的隔阂而产生障碍。正如Martin Fowler在《企业集成模式》一书的序中写道:“集成之所以重要是因为相互独立的应用是没有生命力的。我们需要一种技术能将在设计时并未考虑互操作的应用集成起来,打破它们之间的隔阂,获得比单个应用更多的效益”。这或许是分布式架构存在的主要意义。 在高并发分布式环境下,由于来不及同步处理,通过使用消息队列,可以异步处理请求,从而缓解系统的压力。
举一个订单系统的例子:用户点击下订单,会触发以下业务逻辑流程:
扣减库存
生成相应的订单
发短信通知等等
在业务发展初期这些逻辑可能放在一起同步执行,随着业务订单量增长,需要提升系统服务的性能,这时候可以将一些不需要立即生效的操作拆分出来异步执行,比如发短信通知等,这种场景就可以使用消息队列MQ。
本质还是通过异步来解决同步的系统压力,所以我们在做架构设计的时候有一个原则:能异步的就尽量不要同步。
4、消息队列的选型

1.ActiveMQ

Apache出品,最早使用的消息队列产品,时间比较长了,最近版本更新比较缓慢。
2.RabbitMQ

RabbitMQ是erlang语言开发,结合erlang语言本身的并发优势,支持很多的协议:AMQP,XMPP, SMTP, STOMP,也正是如此,使它变得非常重量级,更适合于企业级的开发。性能较好,但是不利于做二次开发和维护。
3.RocketMQ

阿里开源的消息中间件,纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。
4.ZeroMQ

号称最快的消息队列系统,尤其针对大吞吐量的需求场景。
扩展性好,开发比较灵活,采用C语言实现,实际上只是一个socket库的重新封装,如果做为消息队列使用,需要开发大量的代码。
5.Kafka

Kafka是Apache下的一个子项目,是一个高性能跨语言分布式发布/订阅消息队列系统,而Jafka是在Kafka之上孵化而来的,即Kafka的一个升级版。
5、应用场景
5.1消息通讯
作为消息队列,其场景就是实现消息通讯,
应用举例:微信发消息、QQ聊天群等。
消息队列主要有两种模式:点对点(Point to Point)模式和发布-订阅(Publisher-Subscriber)模式。

点对点模式:
参与者:消息发送者被称为生产者(Producer),消息容器被称之为队列(queue)、消息接收者被称之为消费者(Consumer)。每个消息只有一个消费者,一旦被消费,则从队列中移除。为了让队列移除消息,消费者在消费之后,应该给予队列应答(ACK)。发布-订阅模式:
参与者:消息发送者被称为发布者(Publisher),消息容器被称之为话题(topic)、消息接收者被称之为订阅者(Consumer)。每个消息可有多个订阅者,即使被消费,依然在队列中存在。为了能消费指定话题的消息,订阅者需要订阅该topic;如果不在需要消费其消息,则取消订阅该topic。
5.2异步处理
有时一个操作会涉及多步处理,如果全部同步处理,则耗时很长。
此时,可以将[非关键性业务需要处理]作为消息发送给MQ,然后直接返回操作结果,节省这些业务处理上的耗时。
然后,再通过其他服务异步的消费MQ中的消息,进行非关键性业务的处理。
优点:减低响应时间,提高性能和吞吐量。
应用举例:会员注册之后的短信通知、网站站内信等。
以下图为例:
同步处理:注册流程共耗时=保存用户50ms+短信通知50ms=100ms
异步处理:注册流程共耗时=保存用户50ms+发送消息5ms=55ms

5.3应用解耦
有时一个操作会涉及多个服务,如果其中一个服务挂掉,则整个操作不可用。
此时,可以将[非关键性业务需要处理]作为消息发送给MQ,然后直接返回操作结果,无需在意这些业务是否处理成功。
然后,再通过其他服务异步的消费MQ中的消息,进行非关键性业务的处理。
优点:降低应用耦合性,提升服务可用性和稳定性。
以下图为例:
应用耦合的情况下,如果短信服务发送故障,则整个注册功能都将不可用。应用解耦的情况下,即使短信服务发送故障,注册功能也能继续提供服务。

5.4流量削峰
在秒杀场景,由于峰值访问量过大,超出服务能力,则会压垮服务,导致服务崩溃。
此时,可以先将用户的秒杀请求作为消息存入MQ,然后服务再根据自身能力慢慢处理这些请求。
优点:提示峰值处理能力,提示服务稳定性。
应用举例:秒杀活动、团购抢单等。
以下图为例:
未削峰:秒杀请求峰值为5k,而订单服务的最大吞吐量只有2k,这种情况下定会压垮订单服务。削峰:秒杀请求峰值为5k,MQ的最大吞吐量为100k,处理这些请求绰绰有余;订单服务以2k的速度处理MQ中的请求,服务不会崩溃。

6、常见消息队列及对比
常见消息队列如下:
RabbitMQ:使用Erlang编写的开源消息队列,在AMQP(高级消息队列协议)基础上完成的,以高性能、健壮以及可伸缩性出名,非常重量级,多用于企业级开发。ActiveMQ:Apache软件基金会所研发的开源消息队列,使用Java完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。RocketMQ:阿里巴巴的开源消息队列,用 Java 语言实现,在设计时参考了 Kafka。Kafka:Apache软件基金会的开源消息队列,由Scala和Java编写。常见消息队列对比如下(来源于网络,仅供参考):
| 对比内容 | ActiveMQ | RocketMQ | Kafka | RabbitMQ |
|---|---|---|---|---|
| 所属机构/公司 | Apache | Alibaba | Apache | GoPivotal |
| 是否开源 | 开源 | 开源 | 开源 | 开源 |
| 技术文档完备 | 高 | 高 | 高 | 高 |
| 快速入门 | 提供 | 提供 | 提供 | 提供 |
| 成熟度 | 成熟 | 成熟 | 成熟 | 成熟 |
| 数据可靠性 | 可靠 | 可靠 | 可靠 | 可靠 |
| 集群 | 支持 | 支持 | 支持 | 支持 |
| 负载均衡 | 支持 | 支持 | 支持 | 支持 |
| 部署方式 | 独立 | 独立 | 独立 | 独立 |
| 客户端支持语言 | 多种 | 多种 | 多种 | 多种 |
| 开发语言 | Java | Java | Scala && Java | ErLang |
| 批量操作 | 支持 | 支持 | 支持 | 不支持 |
| 有序性支持 | 支持 | 支持 | 支持 | 单客户端有序 |
| 管理界面 | 一般 | 命令行界面 | 命令行界面 | 较好 |
| 消息延时 | 微秒级 | 毫秒级 | 毫秒级 | 微秒级 |
| 事务支持 | 支持 | 支持 | 不支持 | 不支持 |
| 社区活跃度 | 高 | 中 | 高 | 高 |
| 商业支持 | 无 | 阿里云 | 无 | 无 |
| 协议支持 | AMQP等多种 | 自定义协议 | 自有协议,HTTP | AMQP等多种 |
| 消息丢失概率 | 低 | 无 | 无 | 低 |
| 可用性 | 主从 | 分布式 | 分布式 | 主从 |
| 单机吞吐量 | 万级 | 十万级 | 十万级 | 万级 |
| 部署难度 | 低 | 低 | 中 | 低 |
| 持久化 | 内存、文件、数据库 | 磁盘 | 磁盘,无限数据堆积 | 内存、文件,数据堆积影响效率 |
| 消费方式 | push/pull | push/pull | pull | push/pull |
7、带来的问题
引入消息队列后,我们需要考虑哪些问题呢?
7.1 消息堆积
这是一个很常见的问题,如果消息消费的时间太久,或者服务器故障,导致消息堆积。
7.2 消息丢失
消息堆积一个处理方案就是给消息加上超时时间判定,这样就会衍生一个问题,当消息堆积,处理完成之后,就会存在一定消息的丢失,或者服务器宕机也会导致消息丢失,需要针对此类问题做好应对方案。
7.3 消息准确消费
保证消息被准确消费,且不存在重复消费的问题。
8、总结
ActiveMQ 的社区算是比较成熟,但是较目前来说,ActiveMQ 的性能比较差,而且版本迭代很慢,不推荐使用。
RabbitMQ 在吞吐量方面虽然稍逊于 Kafka 和 RocketMQ ,但是由于它基于 erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。但是也因为 RabbitMQ 基于 erlang 开发,所以国内很少有公司有实力做erlang源码级别的研究和定制。如果业务场景对并发量要求不是太高(十万级、百万级),那这四种消息队列中,RabbitMQ 一定是你的首选。如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。
RocketMQ 阿里出品,Java 系开源项目,源代码我们可以直接阅读,然后可以定制自己公司的MQ,并且 RocketMQ 有阿里巴巴的实际业务场景的实战考验。RocketMQ 社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准 JMS 规范走的有些系统要迁移需要修改大量代码。还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ 挺好的
kafka 的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms 级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时 kafka 最好是支撑较少的 topic 数量即可,保证其超高吞吐量。kafka 唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集。
欢迎大家扫码关注:

本公众号只写原创,不接广告、不接广告、不接广告。下期小伙伴想学习什么技术,可以私信发我吆。





