RocketMQ源码知识讲解前铺垫

什么是消息系统中间件?

中间件被描述为向应用程序提供操作系统所提供的服务之外的服务,简化应用程序的通信、输入输出的开发,使他们更专注于自己的业务逻辑

消息中间件适用于分布式系统下“可靠”的数据交互。采用消息中间件机制的系统中,不同的业务系统之间通过传递消息来激活对方的事件,完成相应的操作。发送者将消息发送给消息服务器,消息服务器将消息进行存储,在合适的时候再将消息转发给接受者,并且可以在不同平台之间通信,屏蔽各种平台以及协议之间的特性。

为什么需要使用MQ中间件?

大家想一想在你们平时开发的系统里面有没有这种情况,就是你们会调用第三方接口服务,而且这个接口服务是同步调用的,这个时候你们的系统性能和可用性是直接和第三方接口服务挂钩的,也就是说第三方接口服务性能的好坏会直接影响到你们自己的系统。

我想大部分人都遇见过这样的系统调用吧,我们公司也经常遇到,一开始我们使用的同步调用,以至于后续流量上来以后,性能吞吐量急剧下降,后来发现是因为第三方接口性能不足以支撑我们现有的并发,之后我们就更改为了MQ异步的方式,至此MQ的第一个优势就体现出来了,异步!

MQ的优势

异步

例如:创建订单需要生成订单号、支付、扣库存、推送短信、扣除会员积分、发送email等多个操作步骤,在如今的分布式系统环境下,每个操作的执行者可能都是其他业务系统,涉及到远程交互,如果直接使用代码调用方式,那么可能会导致服务可用性下降,性能降低。

上述图为未使用MQ中间件的同步操作,可以看到,随着接入的业务系统越来越多,完成一个操作的耗时越来越长,造成用户的体验性降低。

上图为接入消息中间件后的调用链表示,只是简单表示一下,因为业务操作非常复杂,可能涉及多个不同的业务系统,并且不同的业务系统可能依赖其他业务系统的执行结果,因此图可能不是特别准确,但是我们想要解释的目的达到了,那就是异步,将同步操作转换为异步操作。有点类似于线程池提交任务异步执行一样,那大家会存在疑问,既然线程异步也可以完成此操作,为什么需要引入消息中间件呢?是否还有其他的优势?没错!解耦

是线程池异步无法解决的。

解耦

例子1:我们还是以上图的订单为例,上篇文章我们说了线程池异步无法解决解藕问题,假设我们针对上述更改为异步操作,但是此时如果我们又要在下单操作的链路中接入新的业务系统呢?下线存在的业务系统呢?又或者某个业务系统的接口部分更改?这个时候仍然业务系统之间存在耦合,首先是可用性降低,多个业务系统互相依赖,某个业务系统不可用,导致其他系统也不可用,并且涉及到代码的频繁更改,难以维护。

例子2:我们假设存在一个A系统,A系统会不定期的产出一些核心数据,这个时候假设B、C系统依赖A系统产出的核心数据,并且需要准实时,你们会怎样做呢?我们需要注意的是,A系统会不定期的产生数据,有时候可能一天才会产生,有时候会一小时会产生数据,那么比较好的做法其实是A系统产生核心数据后,调用B、C系统并向它们推送数据。而不是让B、C系统定时调用A系统的查询接口。那么问题来了,但是现在又来了系统D、F、G、E、Z等十来个其他系统都需要这份数据怎么办?又或者某个系统不关注此数据了,这个时候A系统的开发人员肯定要崩溃了,因为不停的涉及到代码的改动,难以维护。

因此,针对于业务系统解耦、代码解耦,可以引入消息中间件,那么上游服务只需要产生数据将数据写入到消息中间件,对上游服务数据感兴趣的服务订阅消息中间件的消息即可,不需要频繁的更改代码,业务系统也解除耦合。

上述为未使用MQ进行解藕,业务系统耦合严重,代码耦合严重

上图为使用MQ进行解耦,业务系统耦合性降低,代码耦合性降低

如果是在淘宝这 种核心交易系统,每笔交易订单数据的产生会引入几百上千个下游业务系统的关注,包括物流、购物车、积分、阿里妈妈、流计算分析、大数据等等,整体业务系统庞大而且复杂,架构设计稍有不合理,将直接影响核心交易系统的正常运转。

通过MQ的异步解耦化设计,即使下游系统(如物流、积分等)出现不可用甚至宕机,都会影响核心交易系统的正常运转;通过MQ的异步解耦化设计,也能随时接入新的系统,适应业务的

变化。

流量削峰

诸如秒杀、抢红包、企业开门红等大型活动时皆会带来较高的流量脉冲,或因没做相应的保护而导致系统系统超负荷甚至崩溃,或因限制太过导致大量请求失败而影响用户体验,削峰填谷是解决该问题的最佳方式;

通过 MQ 超高性能的消息处理能力可以承受流量脉冲而不被击垮,在确保系统可用性同时,因快速有效的请求响应响应而提升用户的体验

可以把请求丢到MQ、服务器处理完了再去MQ中取请求,一直重复上述操作,可以让服务器平稳运行。

那大家可能有疑问,为什么不扩展机器呢?部署集群呢?提高服务响应速度呢?

如果说我们的应用每个月可能只存在一次大型活动,其他时间的流量均处于较低的水平,那么我们还需要花费高成本去部署多台机器吗?显然是不合理的,我们应该使用其他手段来削峰,分析出来机器的处理能力,合理的设置阈值。

MQ的劣势

系统可用性下降

由于引入了MQ,并且业务系统可能向MQ发送消息,或者订阅MQ中的消息进行处理,那么MQ的可用性就非常的重要,如果MQ服务不可用,那么可能会影响业务系统的可用性。

系统复杂度提高

加入了MQ消息中间件,需要考虑到更多的问题,比如:数据一致性问题、如何保证消息不重复消费、如何保证消息可靠传输、如何保证MQ服务器高可用,因此需要考

虑的东西更多,复杂性也随之提高。

实现三高的策略(高并发、高可用、高性能)

具体来说,消息中间件只是一种概念和模式,在真正选择并且使用一款MQ产品的时候,要考虑具体落地的MQ是否满足我们的需求?简单来看,可以从以下几个纬度进行思考:

(1) MQ 的性能表现怎么样?是否达到我们的要求?

(2) 假设机器硬件配置相同,比如互联网较常见的4核8G,能扛住多少QPS?什么程度会达到性能瓶颈?

(3)能够实现高可用吗?如果部署的一台机器宕机了,是否存在检测修复机制?

(4)可靠吗?能否保证数据不丢失?

(5)支持线性的集群扩展吗?添加更多机器的机制复杂吗?

(6)功能性是否满足?比如 延迟消息、顺序消息、事物消息、消息回溯、死信队列等?

(7)官方文档是否清晰?社区活跃度怎么样?如果使用过程中出现问题是否网上有现成的解决方案?

(8)是否已经被大量互联网公司实践过了?

(9)是用什么语言写的?是否支持源码级别的个性化定制?

可以根据上述疑问去选择合适的MQ,不同的MQ产品有不同的优势。

消息中间件的种类

目前业界使用最广泛的是Kafka、RocketMq、RabbitMq这三种消息中间件。因此我们主要针对它们来进行对比,接下来我们从 可用性、可靠性、功能性等多方面维度,来分析一下它们的优势和劣势

Kafka

优势

(1)Kafka的吞吐量几乎是行业里最优先的,在常规的机器配置下,一台机器可以达到每秒十几万的QPS,可谓是MQ消息中间件中的顶尖水平上

(2)性能高,向Kafka发送消息可以控制在毫秒级别

(3)可用性高,Kakfa能够支持集群部署,如果部分服务器发生了宕机,仍然可以正常对外提供服务

劣势

(1) Kafka的存储策略是在收到消息之后,将消息写入到磁盘缓冲区内,并不是直接存储到物理磁盘上,这就导致了假如机器本身发生故障,磁盘缓冲区内的数据非常有可能丢失,而消息作为最重要的资源, kafka的这一特点有可能造成严重后果。

(2)Kafka另外一个缺点是功能比较单一,使用了典型的推拉架构设计,无额外的高级功能,不适合用于一些复杂的场景

RabbitMq

再说RabbitMQ,在RocketMQ出现之前,国内大部分公司,包括很多一线互联网大厂都在使用RabbitMQ,而且直到目前,还有很多中小型公司在使用RabbitMQ。

优势

(1)可以有保证数据不丢失的机制。

(2)保证高可用性,集群部署的时候部分,即使部分服务器宕机可以继续执行任务。

(3)实现了部分高级功能,比如死信队列,消息重试。

劣势

(1)RabbitMQ的吞吐量是比较低,只有每秒几万的级别,遇到像双十一这样的高并发场景,很容易到达性能的瓶颈。

(2)维护比较复杂,在集群部署时,如果需要线性扩展,比较麻烦。

(3)它的开发语言是erlang,国内目前大大小小公司的技术骨干大部分都是BAT背景,精通erlang语言的还是少数。阅读源代码非常困难,也就更加无从谈起根据个性化要求修改源代码了。

RocketMq

RocketMQ是阿里开源的消息中间件,经过实战的检验,比较靠谱。后发优势使它在最初设计的时候,就为了去解决Kafka和RabbitMQ所存在的缺陷。

优势

(1)性能强大,吞吐量高,能够达到10万QPS的数量级

(2)可以保证高可用性,能够大规模集群化部署

(3)支持通过配置保证数据不丢失

(4)满足各种需求,功能强大 如:私信队列、顺序消息、延时消息、事物消息、消息积压等

(5)JAVA源码,扩展性高,支持源码定制化

劣势

(1)起步对于Kafka、RabbitMq来说较远,因此社区活跃度可能不如前两者,但是如今国内越来越多的互联网大厂都在使用RocketMq。

如何选择合适的消息中间件

根据Kafka技术在各大公司里的使用情况,目前行业内比较流行的方式是用Kafka进行采集和传输用户行为日志。对于一个电商公司而言,每天的行为数据堪称天量,比如商品详情页的点击,店铺的转化率等等,非常适合大数据团队来收集APP上用户的种种行为。

虽然Kafka有可能会导致消息的丢失,但由于日志本身并不像订单那么重要,即使真的发生了数据丢失也在可控范围内。

最主要在这个场景下,日志量特别大,吞吐量必须要高,只要保证收发消息的正常完成就满足的需要,并不需要其他功能,所以 Kafka非常适合这种场景。

而目前现在国内很多一线互联网大厂,在核心系统模板上,都在慢慢切换为使用RocketMQ了。RocketMQ的高吞吐量,大规模集群部署能力,以及各种高级功能满足了日益复杂的商业化业务需求,尤其是非常适合用在Java业务系统架构中,同时还可以根据自己的需求定制修改RocketMQ的源码。

对于中小型互联网公司,并发量没那么大,似乎看起来RabbitMQ也能满足所有的需要。没有特别高的吞吐量,也不需要部署大规模集群,更没必要阅读和修改RabbitMQ的源码。但是RocketMQ一定是更好的选择,因为它规避掉了RabbitMQ的全部缺点。

RocketMq基本概念

消息模型(Message Model)

RocektMQ主要是由Producer、Broker 、Consumer三部分组成,其中Producer负责生产消息,Consumer负责消费消息,Broker负责存储消息。

Broker在实际部署过程中对应一台服务器,每个Broker可以存储多个Topic的消息,每个Topic的消息也可以分片在不同的Broker上,Message Queue用于存储消息的物理地址,每个Topic中的消息地址存储于多个Message Queue中。ConsumerGroup由多个Consumer实例构成。

消息生产者(Producer)

负责生产消息,一般由业务系统负责生产消息。一个消息生产者会把业务系统里产生的消息发送到Broker服务器上。

RocektMq提供多种发送方式,同步发送、异步发送、顺序发送、单向发送。同步和异步均需要Broker返回确认消息,单向发送不需要。

消息消费者(Consumer)

负责消费消息,一般是后台系统负责异步消息,一个消息消费者会从Broker服务器拉取消息,并将其提供给应用程序,从用户程序的角度而言提供了两种消费形式:拉取式消费、推动式消费。

主题(Topic)

表示一类消息的集合,每个主题包含若干条消息,每条消息只能属于一个主题,是RocekMq进行订阅的基本单位

代理服务器(Broker Server)

消息中转角色,负责存储消息、转发消息。代理服务器在RocketMq系统中负责接受从生产者发送来的消息并存储、同时为消费者的拉取请求作准备。代理服务器也存储消息相关的元数据,包括消费者组,消费者进度偏移和主题和队列消息等

名字服务(Name Server)

Name Server充当路由消息的提供者。生产者或消费者能够通过Name Serve查找各主题相应的Broker IP列表。多个Namesrv实例组成集群,但互相独立,没有信息交换。

拉取式消费 (Pull Consumer)

Consumer消费的一种类型,应用通常主动调用Consumer的拉消息方法向Broker服务器拉取消息、主动权由应用程序控制。一旦获取到了批量消息,应用就会启动消费过程

推动式消费(Push Consumer)

Consumer消费的一种类型,该模式下Broker收到数据主动推送给消费端,该消费模式一般实时性较高,但是也有缺点,具体在后面章节会详细介绍。

生产者组(Producer Group)

同一类Producer的集合,这类Producer发送同一类消息并且发送逻辑一致。如果发送的是事物消息且原始生产者在发送之后崩溃,则Broker服务器会联系同一生产者组的其他生产者实例以提交或回溯消息

消费者组(Consumer Group)

同一类Consumer的集合,这类Consumer通常消费同一类消息并且消费逻辑一致,消费者组的存在使得在消息消费方面,实现负载均衡和容错变得非常容易。要注意的是,消费者组的消费者实例必须订阅完全相同的Topic。

RocketMQ支持两种消费模式:

  • 集群消费(Clustering)
  • 广播消费(Broadcasting)

集群消息 (Clustering)

集群消费模式下,相同Consumer Group每个Consumer实例平均分摊消息

广播消费(Broadcasting)

广播消费模式下,相同Consumer Group的每个Consumer实例都接受全量消息

普通顺序消息(Normal Ordered Message)

普通顺序消费模式下,消费者通过同一个消息队列(Topic分区,称作Message Queue)收到的消息是有顺序的,不同消息队列收到的消息则可能是无序的

严格顺序消息(Strictyl Ordered Message)

严格顺序消息模式下,消费者收到所有消息均是有序的。

消息(Message)

消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须所属一个主题。RocektMq中每个消息拥有唯一的Message id,且可以携带具有业务标识的Key。系统提供了通过Message ID 和 Key 查询消息的功能

标签(Tag)

为消息设置的标志,用于同一主题下区分不同类型的消息。来自同一业务单元的消息,可以根据不同业务目的或者不同业务唯独在同一主题下设置不同标签,标签能够有效地保持代码的清晰度和连贯性,并优化RocektMq提供的查询系统。消费者可以根据Tag实现对不同子主题的不同消费逻辑,实现更好的扩展性。

猜你喜欢

转载自juejin.im/post/7086109998136164366