Nacos - NacosNamingService初始化中提到NacosNamingService初始化會初始化EventDispatcher、NamingProxy、BeatReactor、HostReactor。其中EventDispatcher已經說了,NamingProxy的定時任務主要是預設每30毫秒更新伺服器地址、預設每5毫秒登陸獲取token等資訊,這裡過了。BeatReactor初始化的時候並沒有開啟定時任務,後面來說,那隻剩下HostReactor了。
我們看看他的建構函式,會建立一個FailoverReactor和PushReceiver物件。
public HostReactor(EventDispatcher eventDispatcher, NamingProxy serverProxy, BeatReactor beatReactor,
String cacheDir, boolean loadCacheAtStart, int pollingThreadCount) {
// 其他略
this.failoverReactor = new FailoverReactor(this, cacheDir);
this.pushReceiver = new PushReceiver(this);
}
FailoverReactor
FailoverReactor的建構函式,會呼叫他的init方法:
public FailoverReactor(HostReactor hostReactor, String cacheDir) {
//其他略
this.init();
}
在init裡會有三個任務:
- FailoverReactor.SwitchRefresher,預設每5秒檢測是否開啟故障轉移,如果開啟,則把檔案資料讀入serviceMap。
- FailoverReactor.DiskFileWriter,預設每天把服務資訊寫入本地。
- 建立10秒後呼叫DiskFileWriter#run,檢測本地快取檔案,如果沒有則建立快取檔案。
public void init() {
executorService.scheduleWithFixedDelay(new SwitchRefresher(), 0L, 5000L, TimeUnit.MILLISECONDS);
executorService.scheduleWithFixedDelay(new DiskFileWriter(), 30, DAY_PERIOD_MINUTES, TimeUnit.MINUTES);
// backup file on startup if failover directory is empty.
executorService.schedule(new Runnable() {
//其他略
}, 10000L, TimeUnit.MILLISECONDS);
}
FailoverReactor.SwitchRefresher,預設每5秒檢測是否開啟故障轉移,如果開啟,則把檔案資料讀入serviceMap。
class SwitchRefresher implements Runnable {
long lastModifiedMillis = 0L;
@Override
public void run() {
try {
// 其他略
switchParams.put("failover-mode", "true");
NAMING_LOGGER.info("failover-mode is on");
// 故障轉移的時候呼叫FailoverFileReader#run
new FailoverFileReader().run();
} catch (Throwable e) {
NAMING_LOGGER.error("[NA] failed to read failover switch.", e);
}
}
}
class FailoverFileReader implements Runnable {
@Override
public void run() {
Map<String, ServiceInfo> domMap = new HashMap<String, ServiceInfo>(16);
BufferedReader reader = null;
try {
// 其他略
// 讀取檔案資訊,賦值給dom,存入domMap
for (File file : files) {
// 其他略
ServiceInfo dom = new ServiceInfo(file.getName());
// 其他略
dom = JacksonUtils.toObj(json, ServiceInfo.class);
if (!CollectionUtils.isEmpty(dom.getHosts())) {
domMap.put(dom.getKey(), dom);
}
}
} catch (Exception e) {
NAMING_LOGGER.error("[NA] failed to read cache file", e);
}
// domMap的值賦值給serviceMap
if (domMap.size() > 0) {
serviceMap = domMap;
}
}
}
PushReceiver
PushReceiver實現了Runnable介面,在建構函式中把自己放入了執行緒池。
public PushReceiver(HostReactor hostReactor) {
try {
//其他略
this.executorService.execute(this);
} catch (Exception e) {
NAMING_LOGGER.error("[NA] init udp socket failed", e);
}
}
在run中,透過while一直監聽UDP資料,並根據不同的type進行處理資料,處理後響應請求。
@Override
public void run() {
while (!closed) {
try {
// byte[] is initialized with 0 full filled by default
byte[] buffer = new byte[UDP_MSS];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 獲取UDP資料
udpSocket.receive(packet);
String json = new String(IoUtils.tryDecompress(packet.getData()), UTF_8).trim();
NAMING_LOGGER.info("received push data: " + json + " from " + packet.getAddress().toString());
PushPacket pushPacket = JacksonUtils.toObj(json, PushPacket.class);
String ack;
// 根據不同的type進行處理資料
if ("dom".equals(pushPacket.type) || "service".equals(pushPacket.type)) {
hostReactor.processServiceJson(pushPacket.data);
// send ack to server
ack = "{\"type\": \"push-ack\"" + ", \"lastRefTime\":\"" + pushPacket.lastRefTime + "\", \"data\":"
+ "\"\"}";
} else if ("dump".equals(pushPacket.type)) {
// dump data to server
ack = "{\"type\": \"dump-ack\"" + ", \"lastRefTime\": \"" + pushPacket.lastRefTime + "\", \"data\":"
+ "\"" + StringUtils.escapeJavaScript(JacksonUtils.toJson(hostReactor.getServiceInfoMap()))
+ "\"}";
} else {
// do nothing send ack only
ack = "{\"type\": \"unknown-ack\"" + ", \"lastRefTime\":\"" + pushPacket.lastRefTime
+ "\", \"data\":" + "\"\"}";
}
// 響應請求
udpSocket.send(new DatagramPacket(ack.getBytes(UTF_8), ack.getBytes(UTF_8).length,
packet.getSocketAddress()));
} catch (Exception e) {
NAMING_LOGGER.error("[NA] error while receiving push data", e);
}
}
}
總結
HostReactor的建立任務包括每5秒檢測是否開啟故障轉移,如果開啟,則把檔案資料讀入serviceMap、每天把服務資訊寫入本地、檢測本地快取檔案,如果沒有則建立快取檔案、監聽UDP請求。