Spring Cloud 2.2.2 源碼之四十九nacos客戶端觸發服務刷新原理三
服務獲取和刷新的流程圖
NacosNamingService的selectInstances
前面說了,負載均衡器初始化的時候會最後調用NacosNamingService
的selectInstances
方法,最終到這裏,因爲是訂閱標記是true
,所以直接走hostReactor.getServiceInfo
,內部如果發現服務有更新會進行監聽器的通知,所以叫做訂閱:
HostReactor的getServiceInfo
先判斷容災模式,如果已經出故障了,直接從本地緩存讀取。否則就嘗試獲取服務信息,如果沒有就創建一個,然後立即去註冊中心獲取服務,這裏用了一個updatingMap
來表示再更新的服務,然後進行更新,裏面涉及更新後的結果和老的做比對,如果有改變,會對Notifier
任務進行改變通知,其實就是改變了阻塞隊列changedServices
,Notifier
檢查到他有改變,就會進行處理,去通知監聽器有NamingEvent
事件,不過貌似目前還沒有監聽這個事件的監聽器。完了最後再判斷是否這個服務已開啓UpdateTask
任務更新,沒有的話就開啓一個UpdateTask
任務進行更新,這裏纔是真正更新服務,對已有的服務進行更新,而負載均衡的PollingServerListUpdater
任務,更多的是獲取服務,真正的服務更新還是UpdateTask
任務做的。
public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
String key = ServiceInfo.getKey(serviceName, clusters);
if (failoverReactor.isFailoverSwitch()) {//已經到容災模式
return failoverReactor.getService(key);
}
ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
if (null == serviceObj) {//不存在就創建
serviceObj = new ServiceInfo(serviceName, clusters);
serviceInfoMap.put(serviceObj.getKey(), serviceObj);
//updatingMap表示在更新了
updatingMap.put(serviceName, new Object());
updateServiceNow(serviceName, clusters);
updatingMap.remove(serviceName);
} else if (updatingMap.containsKey(serviceName)) {
//正在更新實例
if (UPDATE_HOLD_INTERVAL > 0) {
// hold a moment waiting for update finish
synchronized (serviceObj) {
try {
serviceObj.wait(UPDATE_HOLD_INTERVAL);
} catch (InterruptedException e) {
NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
}
}
}
}
scheduleUpdateIfAbsent(serviceName, clusters);
return serviceInfoMap.get(serviceObj.getKey());
}
HostReactor的scheduleUpdateIfAbsent
雙重檢測,添加一個UpdateTask
任務。這個任務具體幹什麼,我在前面的文章
裏有,就不多說了。
public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
synchronized (futureMap) {
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
}
}
如果有新的服務獲取到,或者有服務改變了,Notifier
任務會感知到,然後去通知監聽器,不過沒監聽器監聽NamingEvent
事件:
PushReceiver接受推送
一旦註冊中心有服務改變了,就會進行推送,如果發現是dom類型的,表示有更新,所以就調用hostReactor.processServiceJSON
,內部會進行更新,通知等操作,具體跟前面講過的updateServiceNow
的結果處理一樣:
然後根據類型進行相應的應答UDP
報文就好了:
因爲你請求刷新的時候會把UDP
端口傳過去:
好了,服務刷新是如何做到的,是什麼時候刷新的現在基本已經瞭解了吧,除了自己去更新服務信息,服務端還會UDP
推送過來。 流程圖也有了,可以自己debug
一下加深理解,當然還有細節只能自己看啦,面面俱到我也做不到。
好了,今天就到這裏了,希望對學習理解有幫助,大神看見勿噴,僅爲自己的學習理解,能力有限,請多包涵。