原文链接:https://juejin.cn/post/7091096519947862023
作者:pinnuli
一说到RocketMQ,很多人人都知道其有四大组件,NameServer、Broker、Producer、Consumer,也大概都知道这四个组件分别有什么作用,负责什么功能,但是不知道这四个组件是如何配合的。
今天我们就来聊聊,这四个组件如何配合,通过哪些核心的API或者组件进行通讯配合,让RocketMQ跑起来,完成一条消息的产生和消费。
这里只会讲几个组件配合的核心流程和核心API,形成一个整体的框架,以便于在以后遇到问题的时候,能大致定位到问题所在,知道应该去看哪些内容。至于一些源码的深入解读,例如Broker消息是如何存储的,Producer、Consumer的负载均衡算法有哪些,延迟消息、顺序消息等是如何实现的,不是本文重点。
NameServer的作用
NameServer的核心启动流程
我们先看一下NameServer在启动的过程中,做了哪些核心的动作
NamesrvStartup
是NameServer服务的启动类,其main方法中,调用了方法createNamesrvController
创建了一个NamesrvController
createNamesrvController
方法中执行了以下操作
NamesrvConfig
(一些NameServer自身运行的参数,例如配置文件路径等),NettyServerConfig
(一些Netty的参数),并绑定了9876端口,这个端口就是对外提供网络服务的入口;NamesrvController
对象,在对象的构造方法中注入了上面创建的NamesrvConfig
、NettyServerConfig
等对象,以及创建了一个RouteInfoManager,这个对象用于管理Broker的路由信息NamesrvController
,进行初始化,其中两个核心的动作:NettyRemotingServer
,用于处理网络请求,例如Broker的注册和心跳,Producer获取topic的路由数据等,也就是说NameServer基本上是通过这个组件跟外部进行通讯;可以参照源码看一下,还是比较容易看懂的,毕竟是国人写的代码
NameServer的顶层结构
知道了NameServer启动过程中做了哪些事情,可以大概整理出,NameServer在RocketMQ运行过程中,顶层的一些结构。
Broker的作用
整个RocketMQ最核心的部分,消息的存储、转发都由他完成:
1、Producer发送消息给Broker,由Broker负责存储;
2、Consumer从Broker拉消息进行消费(推送模式实际上最终也是由拉消息实现)。
Broker的核心启动流程
我们再看一下Broker在启动的过程中,做了哪些核心的动作,
代码核心流程和NameServer的启动流程非常相似,主要是创建和初始化的组件、定时任务、线程池。
BrokerStartup
是Broker服务的启动类,其main方法中,调用了方法createBrokerController
创建了一个BrokerControllercreateBrokerController
方法中执行了以下操作BrokerConfig
(一些Broker自身运行的参数,例如NameServer地址等等),NettyServerConfig
(一些Netty的参数),作为netty服务端的参数,并绑定了10911端口,这个端口作为接收Producer、Consumer网络请求的入口;NettyClientConfig
(一些Netty的参数),作为netty客户端的参数,;MessageStoreConfig
主要是用来消息存储的一些配置,例如Commitlog文件的大小等;BrokerController
对象,在对象的构造方法中注入了上面创建的BrokerConfig
、NettyServerConfig
、NettyClientConfig
、MessageStoreConfig
等对象,以及创建了一个BrokerOuterAPI,这个对象用于发送客户端请求,例如向NameServer注册、向Producer请求事务消息状态等BrokerController
,进行初始化,其中四类核心的动作:DefaultMessageStore
,用于管理消息在磁盘上的存储NettyRemotingServer
,用于处理网络请求,例如Producer发送过来的消息、Consumer拉去消息等;也就是说Broker用这个组件个Producer、Consumer通讯Broker的顶层结构
上面介绍了Broker在启动过程中,NettyRemotingServer
、BrokerOuterAPI
、MessageStoreConfig
核心对象,至此我们也可以梳理出Broker通过哪些核心对象,与RocketMQ的其他组件配合,完成一条消息的发送、存储、消费。
Producer的启动和使用的核心流程
Produce顶层涉及到的组件不多,我们看下RocketMQ源码中提供的最基本使用例子,根据代码的调用流程,梳理出其启动和发送消息的核心方法,主要在DefaultMQProducerImpl
public static void main(String[] args) throws MQClientException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
producer.start();
for (int i = 0; i < 10000000; i++)
try {
{
Message msg = new Message("TopicTest",
"TagA",
"OrderID188",
"Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
} catch (Exception e) {
e.printStackTrace();
}
producer.shutdown();
}
DefaultMQProducerImpl
MQClientInstance
DefaultMQProducerImpl.sendDefaultImpl()
,这个方法的实现过程才是核心,主要有以下步骤:topicPublishInfoTable()
根据消息的topic的路由信息,会先从本地缓存找,找不到再到NameServer上申请selectOneMessageQueue()
根据topic选择一条队列,默认是负载均衡策略将消息平均分配到每个队列上;sendKernelImpl()
根据消息、Broker信息、获取到的队列等,将消息发送出去,这个方法主要通过MQ客户端实例,即MQClientInstance
发送消息,首先是用BrokerName兑换Broker地址,如果拿不到会去NameServer获取,然后将消息发送出去,至此消息发送完毕。也就是Producer用MQClientInstance
的实现类与其他组件进行通讯,包括从NameServer拉取Broker列表,向Producer发送消息等Producer的顶层结构
我们在将消息的发送过程,加入到上面整理出来的四大组件配合过程中
上面我们说完了NameServer、Broker、Producer,大图中就差最后一个角色Consumer就大功告成了。
Consumer我们也用和Producer差不多的思路去梳理。
Consumer的启动和使用的核心流程
我们同样看下RocketMQ源码中提供的最基本使用例子,其核心逻辑主要在DefaultMQPushConsumerImpl
public static void main(String[] args) throws InterruptedException, MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroupName");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("TopicTest", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.printf("Consumer Started.%n");
}
创建Consumer,订阅某个topic,并注册监听器,核心流程如下:
DefaultMQPushConsumerImpl
,这里会同时创建两个核心组件:PullMessageService
,负责从Broker拉去消息;RebalanceService
,负责控制队列的负载均衡;MQClientInstance
,MQ客户端实例,用于从Broker拉消息、从NameServer拉取Broker列表跟Producer一样,Consumer也是通过这个MQ客户端实例与外部进行通讯;ConsumeMessageService
消息消费组件,会根据不同的消费模式创建不同的实例,例如顺序消费和并发消费,从Broker收到消息后,也是由这个组件完成消费;创建完该组件也会马上启动。MQClientInstance
,在这个过程中会启动PullMessageService
和RebalanceService
Consumer的顶层结构
我们同样把Consumer的消息拉取、消费的核心流程和组件,梳理出其顶层架构
通过组件NettyRemoteServer,绑定9876端口(也可手动设置其他端口),作为服务端,与其他三个组件通信,完成Broker的注册,向Producer、Consumer提供Broker信息;
通过组件NettyRemoteServer,绑定10911端口,作为服务端,接收Producer发消息、Consumer拉消息等网络请求;
通过组件BrokerOuterAPI,作为客户端,向NameServer注册和发送心跳等;
通过MQClientInstance,作为客户端,向NameServer拉去Broker信息、向Broker发送消息;
通过MQClientInstance,作为客户端,向NameServer拉去Broker信息、向Broker拉取消息;