RocketMQ之Broker线程模型

1.概述

RocketMQ是阿里开源的一款高性能、高吞吐量的分布式消息中间件,具有以下特点:

1、能够保证严格的消息顺序;

2、提供丰富的消息拉取模式;

3、高效的订阅者水平扩展能力;

4、实时的消息订阅机制;

5、亿级消息堆积能力。

网上关于RocketMQ各方面(如RPC通信、消息存储、消费发送、消息消费等)的介绍非常多,也比较详细,本文在此就不重复了,感兴趣的同学可以百度一下。

本人从事架构设计多年,经过多年的经验沉淀,总结出一个运行中的软件系统,就如同人的身体一样,是一个有机的整体。一个了解人体奇经八脉的好中医,可以快速洞察人的健康状态,并进行病症诊断。软件运行过程中的线程就像人体的奇经八脉,如果我们清楚地了解软件运行过程中的所有线程,就可以快速确定系统的运行状态,并进行问题诊断。

因此,本文主要从线程的角度,描述一下Broker内部的线程模型,让大家清楚地了解Broker内部结构。

2.RocketMQ部署架构

RocketMQ典型的网络部署架构如下图所示:

RocketMQ之Broker线程模型_第1张图片

在RocketMQ消息队列集群中,主要包括以下几个角色:

(1)NameServer:RocketMQ集群的命名服务器,用于管理集群的元数据,例如:Topic、Broker的注册信息。NameServer是无状态的,可能存在每个NameServer实例上的数据有短暂的不一致现象,但是通过定时更新,在大部分情况下都是一致的;

(2)Broker(Master):RocketMQ消息代理服务器主节点,负责接收Producer发送的消息、消息存储、发送消息到Consumer等;

(3)Broker(Slave):RocketMQ消息代理服务器备份节点,主要是通过同步/异步的方式将主节点的消息同步过来进行备份,为RocketMQ集群的高可用性提供保障;

(4)Producer(消息生产者):主要基于RocketMQ-Client模块将消息发送至RocketMQ的主节点。

(5)Consumer(消息消费者):主要基于RocketMQ-Client模块从RocketMQ的主节点获取消息。

RocketMQ消息队列集群中,各角色之间的关系如下:

(1)Producer与NamerServer:每个Producer与NameServer集群中的一个实例建立TCP连接,从这个NameServer实例上拉取Topic路由信息;

(2)Producer和Broker:Producer与它要发送的topic相关联的Master Broker建立TCP连接,用于发送消息以及心跳信息;

(3)Consumer与NamerServer:每个Consumer与NameServer集群中的一个实例建立TCP连接,从这个NameServer实例上拉取Topic路由信息;

(4)Consumer和Broker:Consumer与它要消费的topic相关联的Master Broker建立TCP连接,用于获取消息以及发送心跳信息;

(5)Broker和NamerServer:Broker(Master or Slave)与每个NameServer建立TCP连接。Broker启动时,注册自己配置的Topic信息到NameServer集群的每一台机器中,即每个NameServer都有该broker的Topic路由配置信息。另外,Master与Master之间无连接,Master与Slave之间有连接,同步或异步同步数据;

3.Broker线程模型

Broker作为RocketMQ中最为重要的部分,承担着整个消息队列集群的80%左右的工作,包括接收Producer发送的消息和心跳信息、消息存储(主要涉及CommitLog、ConsumeQueue、IndexFile)、消息查询、接收Consumer的心跳信息、接收Consumer查询消息、消费消息、发送Broker的Topic配置信息和心跳信息到NameServer等,因此,Broker的性能、伸缩性、可用性、可扩展性对整个消息队列集群起着至关重要的影响。

为了实现高效地与Producer、Consumer、NameServer的网络通信、Broker Master与Slave之间的数据同步、以及消息存储,针对网络通信、数据同步、消息存储,Broker采用了不同的线程模型,以保证Broker高效、稳定的运行。

本文首先通过一张图,从整体上描述Broker内部的线程模型,让大家对Broker有一个整体的概念。然后,分别从RPC通信、消息存储、和消息同步三个方面进行详细的描述。

由于涉及的线程比较多,这里主要介绍每个线程执行的功能,对于内部的具体实现,不在本文的讨论范围之内。如果大家感兴趣,可以自行研究。

首先,RocketMQ Broker内部的线程模型如下图所示:

RocketMQ之Broker线程模型_第2张图片

 

3.1.网络通信线程模型

在RocketMQ消息队列集群部署架构中,Producer、Consumer、Broker和NameServer之间都会发生通信,因此,一个良好的网络通信模块在MQ中至关重要,它将决定RocketMQ集群整体的消息传输能力与性能。

Netty是一个封装了JDK的NIO库的高性能网络通信开源框架。它提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。因此,RocketMQ消息队列基于Netty实现了一个高效的通信模块,实现客户端与服务器之间高效的数据请求与接收。

RocketMQ的RPC通信部分采用了"1+N+M1+M2"的主从Reactor模式,如下图所示:

RocketMQ之Broker线程模型_第3张图片

通常来说,一个完整的网络请求处理过程可以分为接受(accept)、数据读取(read)、解码/编码(decode/encode)、业务处理(process)、发送响应(send)几个阶段。Reactor模型采用SEDA模式,将每个阶段都映射成为一个任务,针对不同的阶段,采用不同的线程执行。这样,服务端线程执行的最小逻辑单元不再是一次完整的网络请求,而是这个任务,且以非阻塞方式执行。

从上图中,我们可以清楚地了解到Broker的RPC通信相关的线程:

1、Boss线程池(NioEventLoopGroup,线程数为1,线程名称为NettyBoss_1):它负责监听TCP网络连接请求事件OP_ACCEPT,建立好连接后,提交注册任务到worker线程池。Boss线程池对应"1+N+M1+M2"中的1;

2、Worker线程池(NioEventLoopGroup或EpollEventLoopGroup,线程数默认为3,线程名称为NettyServerNIOSelector_N_M,N为线程总数,M为顺序号,从1开始,如NettyServerNIOSelector_3_1):从worker线程池中启动一个worker线程,执行register0任务,将socketChannal注册到selector,监听OP_READ事件。当网络数据可读以后,提交任务到executor线程池。Worker线程池对应"1+N+M1+M2"中的N;

3、Executor线程池(DefaultEventExecutorGroup,线程数默认为8,线程名称为NettyServerCodecThread_N,N为线程顺序号,从1开始,如NettyServerCodecThread_1):从Executor线程池,选择一个线程处理Netty网络通信相关的操作,如SSL握手协议、编码、解码、空闲状态管理、连接管理等。Executor线程池对应"1+N+M1+M2"中的M1;

4、业务线程池(BrokerFixedThreadPoolExecutor,继承了ThreadPoolExecutor),针对不同的业务,如发送消息、Pull消息、查询消息、心跳消息、消费者管理等,采用不同的业务线程池。在实际的请求过程中,根据RomotingCommand的业务请求码code去processorTable这个本地缓存变量中找到对应的 processor,然后封装成task任务后,提交给对应的业务processor处理线程池来执行。在Broker中,主要的请求码和对应的处理器如下表所示:

RocketMQ之Broker线程模型_第4张图片

除了上述processor相关的线程池以外,Broker同时启动了ScheduledThreadPoolExecutor线程池,单线程,定时执行以下任务:

  • Broker统计服务:延时N秒执行,每24小时执行一次;
  • Consumer偏移量持久化服务:延时10秒执行,每5秒执行一次;
  • Consumer过滤器管理服务:延时10秒执行,每10秒执行一次;
  • Broker保护服务:延时3分钟执行,每3分钟执行一次;
  • 打印水印服务:延时10秒执行,每1秒执行一次;
  • MessageStore分发服务:延时10秒执行,每60秒执行一次;
  • 获取NameServer地址服务:延时10秒执行,每120秒执行一次;
  • 主从同步服务(如果此Broker为slaver):延时10秒执行,每60秒执行一次;
  • 打印主从差异服务(如果此Broker为master):延时10秒执行,每60秒执行一次;

业务线程池对应"1+N+M1+M2"中的M2;

另外,Broker内部采用了双服务器(NettyRemotingServer)配置,即同时启动remotingServer(监控10911端口)和fastRemotingServer(监控10909端口)两个服务器。这两个服务器除监控的端口不同外,其它完全相同。

3.2.消息存储线程模型

消息存储是RocketMQ消息队列中最复杂,也是最重要的一部分。

RocketMQ选择磁盘文件存储消息。RocketMQ存储层的整体结构主要包括三个数据模型:IndexFile、ConsumerQueue和CommitLog。IndexFile为索引数据文件提供访问服务,ConsumerQueue为逻辑消息队列提供访问服务,CommitLog则为消息存储的日志数据文件提供访问服务。

为了实现高效、可靠的消息存储,针对RocketMQ存储层的三个数据模型,Broker分别采用单独的线程进行处理,如下图所示:

RocketMQ之Broker线程模型_第5张图片

涉及的线程主要包括:

1、CommitLog.FlushRealTimeService:该服务在未使用TransientStorePool情况下,异步刷盘。线程循环运行,每次循环调用waitForRunning方法,默认等待500毫秒。等待超时或由SendMessageThread调用FlushRealTimeService的wakeup方法唤醒;

2、CommitLog.CommitRealTimeService:该服务在使用TransientStorePool情况下,异步刷盘。线程循环运行,每次循环调用waitForRunning方法,默认等待500毫秒。等待超时或由SendMessageThread调用CommitRealTimeService的wakeup方法唤醒;

3、CommitLog.GroupCommitService:该服务主要进行同步刷盘。线程循环运行,每次循环调用waitForRunning方法,默认等待10毫秒。等待超时或由SendMessageThread调用GroupCommitService的wakeup方法唤醒;

4、DefaultMessageStore.ReputMessageService:线程循环运行,每次循环后休眠1毫秒。每个循环依次调用CommitLogDispatcherBuildConsumeQueue和CommitLogDispatcherBuildIndex的dispatch方法实现consumequeue文件的commit操作,即调用channel.write方法和indexfile文件的索引写入操作;

5、DefaultMessageStore.FlushConsumeQueueService:线程循环运行,每个循环调用waitForRunning等待,默认等待1000毫秒。等待超时后继续执行刷新操作。每个循环依次调用consumeQueueTable中每个ConsumeQueue的flush方法,完成ConsumeQueue的刷盘操作;

6、IndexService.FlushIndexFileThread:在ReputMessageService调用CommitLogDispatcherBuildIndex创建索引时,如果现有索引文件已满,则创建新的索引文件,然后启动线程,调用IndexService.this.flush方法,完成上一个索引文件刷盘操作。此线程只执行刷盘操作,操作完成,线程结束;

7、AllocateMappedFileService:线程循环运行,每个循环从请求队列requestQueue获取MappedFile分配任务,如果有任务,则创建MappedFile。如果没有,则阻塞线程,等待任务;

8、StoreStatsService:线程循环运行,每个循环调用waitForRunning等待,默认等待1000毫秒。超时后继续执行。每个循环依次执行存储统计抽样操作和打印存储吞吐量信息;

3.3.消息同步线程模型

RocketMQ为了实现高可用性,Broker建议采用多对Master-Slave模式,即每个Master 配置一个Slave。Master与Slave之间采用同步双写或异步复制方式进行消息同步。

消息同步线程模型如下图所示:

RocketMQ之Broker线程模型_第6张图片

Broker中通过HAService实现,涉及线程如下:

1、HAService.AcceptSocketService

2、HAService.GroupTransferService

3、HAService.HAClient

4、HAConnection.ReadSocketService

5、HAConnection.WriteSocketService

至此,Broker内部线程模型介绍完毕!

由于时间紧,可能存在不足的地方,欢迎大家及时指出!

你可能感兴趣的:(RocketMQ,RocketMQ,消息队列,线程模型,Broker,消息中间件)