我们可以看到在DefaultMessageStore的start方法中通过haService的start()方法启动自己的HA服务。
public void start() throws Exception {
this.acceptSocketService.beginAccept();
this.acceptSocketService.start();
this.groupTransferService.start();
this.haClient.start();
}
先是建立HA服务端监听服务,处理Slave客户端的监听请求。启动AcceptSocketService,处理监听逻辑。启动GroupTransferService线程。最后启动HA客户端。内部的实现会根据Broker配置Master/Slave来决定真正开启的流程,我们先介绍Slave的流程。先重点看下haClient的run()方法
@Override
public void run() {
log.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
try {
if (this.connectMaster()) {
if (this.isTimeToReportOffset()) {
boolean result = this.reportSlaveMaxOffset(this.currentReportedOffset);
if (!result) {
this.closeMaster();
}
}
this.selector.select(1000);
boolean ok = this.processReadEvent();
if (!ok) {
this.closeMaster();
}
if (!reportSlaveMaxOffsetPlus()) {
continue;
}
long interval =
HAService.this.getDefaultMessageStore().getSystemClock().now()
- this.lastWriteTimestamp;
if (interval > HAService.this.getDefaultMessageStore().getMessageStoreConfig()
.getHaHousekeepingInterval()) {
log.warn("HAClient, housekeeping, found this connection[" + this.masterAddress
+ "] expired, " + interval);
this.closeMaster();
log.warn("HAClient, master not response some time, so close connection");
}
} else {
this.waitForRunning(1000 * 5);
}
} catch (Exception e) {
log.warn(this.getServiceName() + " service has exception. ", e);
this.waitForRunning(1000 * 5);
}
}
log.info(this.getServiceName() + " service end");
}
先通过connectMaster()方法,尝试与master的连接
private boolean connectMaster() throws ClosedChannelException {
if (null == socketChannel) {
String addr = this.masterAddress.get();
if (addr != null) {
SocketAddress socketAddress = RemotingUtil.string2SocketAddress(addr);
if (socketAddress != null) {
this.socketChannel = RemotingUtil.connect(socketAddress);
if (this.socketChannel != null) {
this.socketChannel.register(this.selector, SelectionKey.OP_READ);
}
}
}
this.currentReportedOffset = HAService.this.defaultMessageStore.getMaxPhyOffset();
this.lastWriteTimestamp = System.currentTimeMillis();
}
return this.socketChannel != null;
}
得到master的地址,通过remotingUtil连接至masterSocket得到socketChannel,并向其中注册OP_READ的selectKey,之后取得本机的消息存储的最大offset,后续可以跟master交流自己的存储位置。可以看下getMasterPhyOffset的实现,无非取得逻辑文件串MappedFileQueue的最后一个mappedFile的偏移量
public long getMaxOffset() {
MappedFile mappedFile = getLastMappedFile();
if (mappedFile != null) {
return mappedFile.getFileFromOffset() + mappedFile.getReadPosition();
}
return 0;
}
回到haClient的start方法中,在成功地连接了master,并且得到了当前机子上的最大存储消息的offset(currentReportedOffset)后。调用isTimeToReportOffset()方法,判断其与上一次写的时间差是否超过配置的haSendHeartbeatInterval时间间隔,如果超过则调用reportSlaveMaxOffset()并传入currentReportedOffset,向master发送offset并作为心跳。
private boolean reportSlaveMaxOffset(final long maxOffset) {
this.reportOffset.position(0);
this.reportOffset.limit(8);
this.reportOffset.putLong(maxOffset);
this.reportOffset.position(0);
this.reportOffset.limit(8);
for (int i = 0; i < 3 && this.reportOffset.hasRemaining(); i++) {
try {
this.socketChannel.write(this.reportOffset);
} catch (IOException e) {
log.error(this.getServiceName()
+ "reportSlaveMaxOffset this.socketChannel.write exception", e);
return false;
}
}
return !this.reportOffset.hasRemaining();
}
我们可以看到,在这个方法中,slaver向master发送了三次8字节的maxoffset,那在master那边又是如何处理?
上面提到在haServer的start方法中,先调用了acceptSocketServer的beginAccept()方法
public void beginAccept() throws Exception {
this.serverSocketChannel = ServerSocketChannel.open();
this.selector = RemotingUtil.openSelector();
this.serverSocketChannel.socket().setReuseAddress(true);
this.serverSocketChannel.socket().bind(this.socketAddressListen);
this.serverSocketChannel.configureBlocking(false);
this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
}
先开启master的ServerSocketChannel,再开启selector并在其中注册ServerSocketChannel通道并且是监听ON_ACCEPT事件,其中非阻塞,ReuseAddress。我们再继续看acceptSocketServer的run方法。
@Override
public void run() {
log.info(this.getServiceName() + " service started");
while (!this.isStopped()) {
try {
this.selector.select(1000);
Set<SelectionKey> selected = this.selector.selectedKeys();
if (selected != null) {
for (SelectionKey k : selected) {
if ((k.readyOps() & SelectionKey.OP_ACCEPT) != 0) {
SocketChannel sc = ((ServerSocketChannel) k.channel()).accept();
if