之前几篇文章讲了Producer如何发送消息,Consumer如何收消息。后面会用更多的几篇来讲Broker,其实也就是消息队列的核心-分布式Queue的实现。
Broker的功能主要包含如下几点:
- 接收Producer发送的消息
- 存储消息
- 回复consumer的消息拉取请求
- master-slave之间的数据同步
- 提供查询消息的接口
首先看下Broker主要的类的关系
Broker核心类
数据管理类
Broker配置数据的缓存类,所有数据都以本地文件的方式存在broker上,同时会缓存全量数据在内存中。
TopicConfigManager:管理所有broker上存在的topic以及queue的信息。topic的数据会定时和nameserv做同步,以更新Nameserv上的topic路由信息。
ConsumeOffsetManager :管理每个consumer group消息消费的进度。Cluster的consumer会在消息消费成功后把offset信息同步给broker
ProducerManager:producer客户端信息
ConsumerManager:consumer客户端信息
ConsumerFilterManager:consumer filter server信息
SubscriptionGroupManager:consumer订阅信息
请求processor
客户端请求处理类,处理消息请求、配置及控制命令请求,最核心的就是SendMessageProcessor和PullMessageProcessor,一个收producer消息,一个将消息response给consumer。
SendMessageProcessor:接收Producer发来的消息,调用存储模块存储
PullMessageProcessor :接受Consumer的消息Pull请求,返回符合条件的消息
QueryMessageProcessor:接受历史消息查询请求,支持按Msg ID、Msg Key和时间段查询消息详情
AdminBrokerProcessor:接受管理命令,如topic创建等
MessageStore
消息存储接口,这个是Broker的核心,提供消息读写。
CommitLog:消息详情存储,同一个broker上的所有消息都保存在一起,每条消息保存后都会有一个offset
ConsumeQueue:按topic和queue存储消息,相同topic和queue的消息存储在一起,内容是commitLog的offset。consumer是按照queue来拉取消息的,所以都是先读取consumeQueue拿到offset的列表,然后到commitLog读取消息详情
IndexService:CommitLog的索引文件,通过对Msg Key创建索引文件,来快速的定位消息。
ReputMessageService:负责读取CommitLog文件中的消息,分发给ConsumeQueue和IndexService构建Queue和索引文件
HAService:负责消息的主从同步,对于master来说,管理所有slave并发送新的消息数据给slave
SlaveSynchronize
负责同步broker配置数据,不会同步消息数据
PullRequestHoldService
对于PushConsumer,当读取的时候没有发现消息,该类会暂时hold住请求,当有新的消息到达的时候,再回复请求。
Broker的启动过程
Broker启动主要由BrokerController完成初始化和启动的过程,下面看下具体过程BrokerController.initialize()
:
初始化
public boolean initialize() throws CloneNotSupportedException {
//从持久化文件中加载数据到内存中
boolean result = this.topicConfigManager.load();
result = result && this.consumerOffsetManager.load();
result = result && this.subscriptionGroupManager.load();
result = result && this.consumerFilterManager.load();
if (result) {
try {
//消息存取的核心接口初始化,提供put/get message接口,提供根据offset获取消息的接口
this.messageStore =
new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener,
this.brokerConfig);
//messageStore的指标统计类,提供最近一天的消息吞吐量的统计数据
this.brokerStats = new BrokerStats((DefaultMessageStore) this.messageStore);
//load plugin
MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig);
this.messageStore = MessageStoreFactory.build(context, this.messageStore);
//添加消息分发器,分发到布隆过滤器
this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));
} catch (IOException e) {
result = false;
log.error("Failed to initialize", e);
}
}
//message store加载内存映射文件,commit log文件,consumer queue文件,index文件
result = result && this.messageStore.load();
if (result) {
//初始化netty server
this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);
NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone();
//传说中的VIP channel,端口是broker端口-2(10909),不接收consumer的Pull请求
fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2);
this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService);
//初始化一系列客户端命令执行的线程池
this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor(
this.brokerConfig.getSendMessageThreadPoolNums(),
this.brokerConfig.getSendMessageThreadPoolNums(),
1000 * 60,
TimeUnit.MILLISECONDS,
this.sendThreadPoolQueue,
new ThreadFactoryImpl("SendMessageThread_"));
...
...
//注册消息/命令处理器,其中最重要的就是SendMessageProcessor和PullMessageProcessor,一个收producer消息一个发给consumer
this.registerProcessor();
//打印broker的消息吞吐信息到日志文件,每天0点记录一次
final long initialDelay = UtilAll.computNextMorningTimeMillis() - System.currentTimeMillis();
final long period = 1000 * 60 * 60 * 24;
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.getBrokerStats().record();
} catch (Throwable e) {
log.error("schedule record error.", e);
}
}
}, initialDelay, period, TimeUnit.MILLISECONDS);
//记录consumerOffet到文件,默认5秒一次
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerOffsetManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumerOffset error.", e);
}
}
}, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
//记录consumer filter到文件中,10秒一次
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerFilterManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumer filter error.", e);
}
}
}, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
//定时检查consumer的消费记录,如果延时太大,则disable consumer,不再往这个consumer投递消息
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.protectBroker();
} catch (Throwable e) {
log.error("protectBroker error.", e);
}
}
}, 3, 3, TimeUnit.MINUTES);
//打印当前Queue size日志
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.printWaterMark();
} catch (Throwable e) {
log.error("printWaterMark error.", e);
}
}
}, 10, 1, TimeUnit.SECONDS);
//打印dispatch落后情况
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());
} catch (Throwable e) {
log.error("schedule dispatchBehindBytes error.", e);
}
}
}, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
//更新nameserv address信息
if (this.brokerConfig.getNamesrvAddr() != null) {
this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());
log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());
} else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.brokerOuterAPI.fetchNameServerAddr();
} catch (Throwable e) {
log.error("ScheduledTask fetchNameServerAddr exception", e);
}
}
}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
}
//如果是slave,启动定时任务,每分钟从master同步配置和offset
if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {
this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());
this.updateMasterHAServerAddrPeriodically = false;
} else {
this.updateMasterHAServerAddrPeriodically = true;
}
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.slaveSynchronize.syncAll();
} catch (Throwable e) {
log.error("ScheduledTask syncAll slave exception", e);
}
}
}, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
} else {//如果是master,定时打印slave延时情况
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.printMasterAndSlaveDiff();
} catch (Throwable e) {
log.error("schedule printMasterAndSlaveDiff error.", e);
}
}
}, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
}
//TLS链接,监控签名文件有没有更新
if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
// Register a listener to reload SslContext
...
...
}
//初始化事务消息service
initialTransaction();
}
return result;
}
初始化过程一共做如下几件事情:
- 从持久化文件中加载数据,包括topic配置数据,consumer offset数据,consumer订阅配置数据和过滤配置等等
- 初始化messageStore,MessageStore中commitLog、ConsumeQueue和index文件都是通过内存映射文件的方式来读写的。所以初始化的时候会做文件映射的初始化
- 初始化Netty Server,Broker会同时监听两个端口,默认是10911和10909,其中10909只处理Producer的消息发送请求,所以broker上称为fastServer。客户端称之为VIP Channel
- 注册请求的processor
- 初始化一些定时任务,包括记录日志、consumer offset持久化、consume延迟检查、更新nameserv地址、slave的配置数据同步等
启动
启动的过程在BrokerController.start()
方法中实现
public void start() throws Exception {
//启动消息存储服务
if (this.messageStore != null) {
this.messageStore.start();
}
//启动netty server接收请求
if (this.remotingServer != null) {
this.remotingServer.start();
}
//启动fast netty server
if (this.fastRemotingServer != null) {
this.fastRemotingServer.start();
}
//启动tls签名文件检测服务
if (this.fileWatchService != null) {
this.fileWatchService.start();
}
//broker netty client
if (this.brokerOuterAPI != null) {
this.brokerOuterAPI.start();
}
//启动PushConsumer的请求hold服务
if (this.pullRequestHoldService != null) {
this.pullRequestHoldService.start();
}
//监控客户端连接,定时检查Producer,Consumer和Filter是否长时间未收到心跳
if (this.clientHousekeepingService != null) {
this.clientHousekeepingService.start();
}
//启动Filter Server
if (this.filterServerManager != null) {
this.filterServerManager.start();
}
//注册broker到nameserv
this.registerBrokerAll(true, false, true);
//周期性向nameserv发心跳,如果有变化则同步broker信息
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
} catch (Throwable e) {
log.error("registerBrokerAll Exception", e);
}
}
}, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);
if (this.brokerStatsManager != null) {
this.brokerStatsManager.start(); //do nothing
}
//启动brokerFastFailure,定时清理长时间未执行的客户端请求
if (this.brokerFastFailure != null) {
this.brokerFastFailure.start();//启动超时检查
}
//如果是Master,开启事务消息检查
if (BrokerRole.SLAVE != messageStoreConfig.getBrokerRole()) {
if (this.transactionalMessageCheckService != null) {
log.info("Start transaction service!");
this.transactionalMessageCheckService.start();
}
}
}
以上的启动过程比较简单,最重要的就是第一步中的MessageStore的启动,具体实现在DefaultMessageStore.start()
中,下一篇文章我们将重点讲MessageStore数据存储的结构以及启动的过程。