Nacos服務(wù)發(fā)現(xiàn)并發(fā)啟動(dòng)scheduleUpdate定時(shí)任務(wù)的流程分析
一、前言
博主今天復(fù)習(xí)Nacos源碼的時(shí)候,發(fā)現(xiàn)了一個(gè)算是有意思的細(xì)節(jié)點(diǎn),這里分享一下。
二、服務(wù)發(fā)現(xiàn)“懶加載”機(jī)制
當(dāng)nacos客戶端運(yùn)?起來之后,它只是去做服務(wù)注冊(cè)、配置獲取等操作;并不會(huì)立即去請(qǐng)求服務(wù)信息;當(dāng)?shù)谝淮握?qǐng)求時(shí)候,才會(huì)去獲取服務(wù),即懶加載機(jī)制;
1、服務(wù)發(fā)現(xiàn)流程

Client端做服務(wù)發(fā)現(xiàn)時(shí),無論是否能從本地緩存中獲取到服務(wù)實(shí)例信息,都會(huì)啟動(dòng)一個(gè)定時(shí)任務(wù)(每秒做一次服務(wù)實(shí)例信息的更新)。然而由于可能多個(gè)請(qǐng)求同時(shí)要做服務(wù)發(fā)現(xiàn),這樣總不能每一次做服務(wù)發(fā)現(xiàn)時(shí)都去啟動(dòng)一個(gè)定時(shí)任務(wù)做服務(wù)實(shí)例信息更新吧。所以肯定要有一個(gè)機(jī)制去確保針對(duì)某一個(gè)Cluster下的Service,當(dāng)前Client只會(huì)啟動(dòng)一個(gè)定時(shí)任務(wù)。
2、HostReactor#scheduleUpdateIfAbsent()
HostReactor#scheduleUpdateIfAbsent()方法負(fù)責(zé)啟動(dòng)做服務(wù)實(shí)例信息更新的定時(shí)任務(wù)。
從方法名也能看出來:是當(dāng)定時(shí)任務(wù)不存在時(shí),才會(huì)啟動(dòng)一個(gè)定時(shí)任務(wù)。

private final Map<String, ScheduledFuture<?>> futureMap = new HashMap<String, ScheduledFuture<?>>();
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);
}
}看一下這個(gè)代碼邏輯:
- 為了防止一個(gè)實(shí)例開啟多個(gè)這種定時(shí)任務(wù),會(huì)用一個(gè)Map來去重,針對(duì)這個(gè)Map的數(shù)據(jù)添加會(huì)采用一個(gè)類似雙重檢查鎖的機(jī)制,確保Map添加數(shù)據(jù)的線程安全性。
- 如果futureMap中已經(jīng)有了當(dāng)前服務(wù),則方法直接返回。
- 這一步是為了減少不必要的同步操作。
- 否則,對(duì)futureMap對(duì)象加synchronized鎖;同步代碼塊需要再進(jìn)一步判斷futureMap不為空。
- 因?yàn)樵诙嗑€程并發(fā)場景下,可能多個(gè)線程判斷futureMap中都沒有當(dāng)前服務(wù),都進(jìn)入到同步代碼塊,一個(gè)線程進(jìn)入同步代碼塊,將當(dāng)前服務(wù)添加到futureMap中。
- 其他線程獲取到鎖之后再進(jìn)入到同步代碼塊,再次判斷futureMap中是否含有當(dāng)前服務(wù),如果含有,則方法直接返回。
- 假如同步代碼中不判空,會(huì)重復(fù)執(zhí)行將當(dāng)前服務(wù)添加到futureMap中的操作。
既然是DCL的變種版,為了futureMap沒有被volatile關(guān)鍵字修飾,不會(huì)有指令重排序的問題嗎?
- 并不會(huì),futureMap.put()是Map的添加數(shù)據(jù)操作,和對(duì)象初始化是兩種場景。
3、DCL
Double check Lock確保類單例的代碼如下:
public class DoubleCheckLazySingleton {
private volatile static DoubleCheckLazySingleton singleton;
private DoubleCheckLazySingleton() {
}
public DoubleCheckLazySingleton getSingleton() {
if (singleton == null) {
synchronized (this) {
//只有在singleton為null的時(shí)候才創(chuàng)建實(shí)例
if (singleton == null) {
singleton = new DoubleCheckLazySingleton();
}
}
}
return singleton;
}
}到此這篇關(guān)于Nacos服務(wù)發(fā)現(xiàn)并發(fā)啟動(dòng)scheduleUpdate定時(shí)任務(wù)的文章就介紹到這了,更多相關(guān)Nacos服務(wù)發(fā)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot ApplicationEvent之事件發(fā)布與監(jiān)聽機(jī)制詳解
本文將深入探討SpringBoot的事件機(jī)制,介紹其核心概念、實(shí)現(xiàn)方法及最佳實(shí)踐,幫助開發(fā)者構(gòu)建更加靈活、可維護(hù)的應(yīng)用架構(gòu),希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
SpringBoot集成RabbitMQ實(shí)現(xiàn)用戶注冊(cè)的示例代碼
這篇文章主要介紹了SpringBoot集成RabbitMQ實(shí)現(xiàn)用戶注冊(cè)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Java連接數(shù)據(jù)庫實(shí)現(xiàn)方式
文章講述了Java連接MySQL數(shù)據(jù)庫的詳細(xì)步驟,包括下載和導(dǎo)入JDBC驅(qū)動(dòng)、創(chuàng)建數(shù)據(jù)庫和表、以及編寫連接和讀取數(shù)據(jù)的代碼2024-11-11
Java解析調(diào)用webservice服務(wù)的返回XML串詳解
這篇文章主要介紹了Java解析調(diào)用webservice服務(wù)的返回XML串詳解的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
SpringBoot+Vue靜態(tài)資源刷新后無法訪問的問題解決方案
這篇文章主要介紹了SpringBoot+Vue靜態(tài)資源刷新后無法訪問的問題解決方案,文中通過代碼示例和圖文講解的非常詳細(xì),對(duì)大家解決問題有一定的幫助,需要的朋友可以參考下2024-05-05
使用新版Maven-mvnd快速構(gòu)建項(xiàng)目
本文主要介紹了使用新版Maven-mvnd來快速構(gòu)建項(xiàng)目,相比于Maven,mvnd可以顯著提高構(gòu)建速度,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01

