前言
接下来我在写一些技术类科普的文章,大致会以who(它是谁)、why(为什么)、how(怎么做)的写作方向来向大家介绍说明,因为我认为这样子介绍说明思路会比较明确,也能够更快学会一项新技能,个人拙见,写得不好、不对的地方,还望大家赐教。
Who|什么是消息队列(MQ)
在计算机科学中,消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通常是来自用户。消息队列提供了异步的通信协议,每一个贮列中的纪录包含详细说明的资料,包含发生的时间,输入设备的种类,以及特定的输入参数,也就是说:消息的发送者和接收者不需要同时与消息队列交互。消息会保存在队列中,直到接收者取回它。[1]
一个 WIMP 环境像是 Microsoft Windows,借由优先的某些形式(通常是事件的时间或是重要性的顺序)来存储用户产生的事件到一个 事件贮列 中。系统把每个事件从事件贮列中传递给目标的应用程序。
—*
MQ(Message Queue)消息队列,是基础数据结构中“先进先出”的一种数据结构。指把要传输的数据(消息)放在队列中,用队列机制来实现消息传递——生产者产生消息并把消息放入队列,然后由消费者去处理。消费者可以到指定队列拉取消息,或者订阅相应的队列,由MQ服务端给其推送消息。
—百度百科
简单来说,就是排队的意思,先进先出。
Why|为什么用MQ
查个题外话,我认为使用一项新技术的时候,我们要综合考虑该技术的利弊性、维护性、成本性,而不是盲目跟随主流,自己的业务产品不管三七二十一都使用最新的技术,也得根据项目的实际情况来最终决定。
回归主题,存在即合理,它的主要的三大应用场景:应用解耦、异步消息、流量削峰,除此之外,还有延迟通知、分布式事务、顺序消息、流式处理等等。下面我主要细说下它的三大应用场景。
应用解耦
一个业务需要多个模块共同实现,或者一条消息有多个系统需要对应处理,只需要主业务完成以后,发送一条MQ,其余模块消费MQ消息,即可实现业务,降低模块之间的耦合。
举个例子,有个业务场景,我有三个系统,分别是A、B、C系统,大致业务是,A系统操作完具体业务,会分别调用B跟C的接口。
传统做法
正常情况,我们会直接在A系统的代码里面写上对B、C系统的接口请求。如下图
可能存在问题:
1、假设其中一个系统因为不确定因素导致宕机了,这时候是不是整个业务都走不下去了
2、假设业务突然又需要增加调用一个D系统,或者去除C系统的调用,那是不是代码又要重新改一次
3、改任何一个系统的代码,都要小心翼翼的
因此根据以上几点,我们可以明显看出应用系统之间的耦合度很高,没有很独立的思想,所以就需要引用MQ来作为中间件来降低它们之间的耦合度。
MQ做法
A系统就产生一条数据发送到MQ,这时候BCD系统哪个需要这个数据就自行去消费即可,如果又新增或减少好几个应用系统,只需要在去消费或者取消消费。
由此可见,这样子A系统是不是很独立了,压根就不需要管BCD系统会出现什么异常情况,A系统的代码也不再需要重新维护。
异步处理
主业务执行结束后从属业务通过MQ,异步执行,减低业务的响应时间,提高用户体验。
举个例子,有个预约挂号的业务,病人挂号成功后,需要发送短信通知跟微信通知
传统做法
假设都正常执行,用户挂号操作调用挂号系统接口耗时200ms,然后在调用短信接口200ms,再调用微信通知接口200ms,那这样子加起来一共就要600ms,这是正常情况,假如出现某个接口很耗时,那这样子就很影响用户体验了
MQ做法
挂号系统接口是必须的,还是200ms,然后挂号系统在产生消息到MQ花费3ms,短信接口跟微信系统到时候在自行消费,那响应速度一下子变成快了近2倍
流量削峰
高并发情况下,业务异步处理,提供高峰期业务处理能力,避免系统瘫痪。
举个例子,A系统每天都风平浪静的,每秒平均的并发量也才50个,但是有一天,在某个时间段,突然每秒的并发请求量激增到6K+,假如该系统的数据库是用mysql,每秒执行6K+条的SQL。正常的mysql数据库能够抗住每秒2k条的执行语句,一下子到6k+,会直接导致数据库崩溃,从而导致系统也崩溃了。
传统做法
MQ做法
引用 MQ,直接将全部的请求(每秒 6k+) 个请求写入 MQ,因为我们的A系统最大的承载量是每秒2k个请求,所以每次从MQ中慢慢拉取,一次就拉取2k请求,不超过自己系统的最大请求量就行了。这样子,即使在高峰的请求量,也不用在怕A系统会崩溃。虽然在高峰期的时候,这些请求可能堆积在MQ中有几十万甚至几百万条,但是只要一过高峰期,从MQ每秒2k条的拉取执行,很快也会被消化完毕的。
MQ有什么优缺点
东西都是有两面性的。优点就不用说了吧,就是上面说的那些,下面我们来说说有哪些缺点跟需要考虑的问题
- 系统可⽤性降低
系统引⼊的外部依赖越多,越容易挂掉。本来 A 系统调⽤ BCD 三个系统的接⼝就好了,没啥问题,但偏偏加个 MQ 进来,万⼀ MQ 挂了咋整,MQ ⼀挂,不仅整套系统崩溃的,你也就崩溃?所以要保证消息队列的⾼可⽤。
- 系统复杂度提⾼
硬⽣⽣加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?
- ⼀致性问题
A 系统处理完了直接返回成功了,都以为这个请求就成功了;但是问题是,要是 BCD三个系统中,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?这时候数据就不⼀致了。
总结一下:
-
如何保证消息的高可用
-
如何保证消息消费的幂等性
-
如何处理消息丢失问题
-
如何保证消息的顺序性
-
如何解决消息积压
-
如何保持数据一致性
How|怎么用MQ
目前市场常见的消息队列产品主要有ActiveMQ、RabbitMQ、RocketMQ、Kafka等。
那么怎么选择一款符合自己产品的MQ就很关键了。下面有个对比分享做参考。
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
开发语言 | Java | Erlang | Java | Scala&Java |
客户端支持语言 | Java、C、C++、Python、PHP、Pert、.net等 | 几乎支持所有常用语言 | Java、C++ | 官方支持Java,但是开源社区有常用语言版本 |
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic 数量对吞吐量的影响 | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 | ||
时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
综上,各种对比之后,有如下建议:
- ActiveMQ最为老牌MQ,现在用的人不多,社区也不太活跃,所有不推荐
- 中小企业,并发量不是很大的,追求稳定,RabbitMQ是首选
- Java首选RocketMQ,毕竟阿里出品,向大厂看齐
- 大数据领域的用 Kafka 是业内标准的,社区活跃度很高