[ 消息队列- RocketMQ | 青训营笔记 ]

132 阅读8分钟

一、RocketMQ 为什么有了kafka,阿里还要自建mq? 随着业务吞吐量的增长,在使用越来越多的队列和虚拟主题的情况下,ActiveMQ IO模块遇到了瓶颈。此时,业界出现了现在被很多大数据领域所推崇的Kafka消息引擎,但是Kafka不能满足阿里巴巴的要求,特别是在低延迟和高可靠性方面。相关的说明可以在官方文档中查看。

Kafka是一个分布式流媒体平台,诞生于日志聚合案例。它不需要太高的并发性。在阿里巴巴的一些大型案例中,我们发现原来的模型已经不能满足我们的实际需求。

因此,我们开发了一个名为 RocketMQ 的消息中间件,它可以处理广泛的用例,从传统的发布/订阅场景到要求苛刻的大容量实时事务系统,不能容忍任何消息丢失。现在,在阿里巴巴,RocketMQ 集群每天处理超过 5000 亿个事件,为超过 3000 个核心应用提供服务。

RockeMQ连接池

1.1 RocketMQ消息怎么保证可靠性以及高可用性 阿里云MQ消息重试机制验证 经测试,消费者短时间内返回消费失败结果的情况下,得出如下结论:

消费者未能正常消费时,MQ将重新投递消息; 消费者重试时间间隔与阿里云官方文档基本一致; 消费者重试超过16次之后不再进行重试,与阿里云官方文档一致; help.aliyun.com/document_de…

developer.aliyun.com/article/530…

www.bilibili.com/video/BV1TE…

www.bilibili.com/video/BV1Ap…

高吞吐 www.ijiandao.com/2b/baijia/9… 吞吐方面,在小包非批量以及大量分区的场景下(现实应用更广泛的场景),RocketMQ 更能充分利用磁盘的 IO 能力达到更高的 TPS(领先 Kafka 一倍左右)。在大包和批量的场景下,RocketMQ 和 Kafka 目前已经相差无几,此时的瓶颈已经转移到磁盘的吞吐能力上。

1.2 RocketMQ消费协议与模式(push pull) 1.tcp

tcp在内网

2.http

HTTP协议公网接入点访问消息队列

rocketmq分为push与pull

push模式:推送模式,即服务端有数据之后立马推送消息给客户端,需要客户端和服务器建立长连接,实时性很高,对客户端来说也简单,接收处理消息即可;缺点就是服务端不知道客户端处理消息的能力,可能会导致数据积压,同时也增加了服务端的工作量,影响服务端的性能;

pull模式:拉取模式,即客户端主动去服务端拉取数据,主动权在客户端,拉取数据,然后处理数据,再拉取数据,一直循环下去,具体拉取数据的时间间隔不好设定,太短可能会导致大量的连接拉取不到数据,太长导致数据接收不及时;

RocketMQ使用了长轮询的方式,兼顾了push和pull两种模式的优点,下面首先对长轮询做简单介绍,进而分析RocketMQ内置的长轮询模式。

1.3 RocketMQ的消费模式 集群消费 当 consumer 使用集群消费时,每条消息只会被 consumer 集群内的任意一个 consumer 实例消费一次。举个例子,当一个 consumer 集群内有 3 个consumer 实例(假设为consumer 1、consumer 2、consumer 3)时,一条消息投递过来,只会被consumer 1、consumer 2、consumer 3中的一个消费。

同时记住一点,使用集群消费的时候,consumer 的消费进度是存储在 broker 上,consumer 自身是不存储消费进度的。消息进度存储在 broker 上的好处在于,当你 consumer 集群是扩大或者缩小时,由于消费进度统一在broker上,消息重复的概率会被大大降低了。

注意:在集群消费模式下,并不能保证每一次消息失败重投都投递到同一个 consumer 实例。

广播消费 与集群消费不同的是,consumer 的消费进度是存储在各个 consumer 实例上,这就容易造成消息重复。还有很重要的一点,对于广播消费来说,是不会进行消费失败重投的,所以在 consumer 端消费逻辑处理时,需要额外关注消费失败的情况。

虽然广播消费能保证集群内每个 consumer 实例都能消费消息,但是消费进度的维护、不具备消息重投的机制大大影响了实际的使用。因此,在实际使用中,更推荐使用集群消费,因为集群消费不仅拥有消费进度存储的可靠性,还具有消息重投的机制。而且,我们通过集群消费也可以达到广播消费的效果。

使用集群消费模拟广播消费 如果业务上确实需要使用广播消费,那么我们可以通过创建多个 consumer 实例,每个 consumer 实例属于不同的 consumer group,但是它们都订阅同一个 topic。

举个例子,我们创建 3 个 consumer 实例,consumer 1(属于consumer group 1)、consumer 2(属于 consumer group 2)、consumer 3(属于consumer group 3),它们都订阅了 topic A ,那么当 producer 发送一条消息到 topic A 上时,由于 3 个consumer 属于不同的 consumer group,所以 3 个consumer都能收到消息,也就达到了广播消费的效果了。

除此之外,每个 consumer 实例的消费逻辑可以一样也可以不一样,每个consumer group还可以根据需要增加 consumer 实例,比起广播消费来说更加灵活。

rocketmq 架构组成 他主要有四大核心组成部分:NameServer、Broker、Producer以及Consumer四部分。

架构设计 保证高可用 NameServer VS zk

我们可以对比下kafka和rocketMq在协调节点选择上的差异,kafka通过zookeeper来进行协调,而rocketMq通过自身的namesrv进行协调。

kafka在具备选举功能(CP),在Kafka里面,Master/Slave的选举,有2步:第1步,先通过ZK在所有机器中,选举出一个KafkaController;第2步,再由这个Controller,决定每个partition的Master是谁,Slave是谁。因为有了选举功能,所以kafka某个partition的master挂了,该partition对应的某个slave会升级为主对外提供服务。

rocketMQ不具备选举(AP),Master/Slave的角色也是固定的。当一个Master挂了之后,你可以写到其他Master上,但不能让一个Slave切换成Master。那么rocketMq是如何实现高可用的呢,其实很简单,rocketMq的所有broker节点的角色都是一样,上面分配的topic和对应的queue的数量也是一样的,Mq只能保证当一个broker挂了,把原本写到这个broker的请求迁移到其他broker上面,而并不是这个broker对应的slave升级为主。

rocketMq在协调节点的设计上显得更加轻量,用了另外一种方式解决高可用的问题,思路也是可以借鉴的。

rocketmq是通过多个master实现写入容灾,通过主从实现读取容灾(这一组Broker的Master挂了,但是这组中的Slave可以继续提供读的服务,直至把未消费完的消息全部读完;这一组的Master挂了,写的服务会找另一组的Master继续写)

RocketMQ由几部分组成以及每个组件的作用。

RocketMQ架构上主要分为四部分,如上图所示:

Producer

消息发布的角色,支持分布式集群方式部署。Producer通过MQ的负载均衡模块选择相应的Broker集群队列进行消息投递,投递的过程支持快速失败并且低延迟。

Consumer

消息消费的角色,支持分布式集群方式部署。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制,可以满足大多数用户的需求。

NameServer

NameServer是一个非常简单的Topic路由注册中心,其角色类似Kafka中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能:

Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活; 路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。 NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。(保持长链接) 路由信息是包括了:BokerServer,Topic和ConsumeQueueID等信息。

当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer,Consumer仍然可以动态感知Broker的路由的信息。

BrokerServer

Broker主要负责消息的存储、投递和查询以及服务高可用保证,为了实现这些功能,Broker包含了以下几个重要子模块。