chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Nacos為什么這么強(qiáng)?Nacos注冊(cè)中心的底層原理,從服務(wù)注冊(cè)到服務(wù)發(fā)現(xiàn)

電子工程師 ? 來(lái)源:碼猿技術(shù)專欄 ? 作者:碼猿技術(shù)專欄 ? 2022-10-08 16:46 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

來(lái)源:碼猿技術(shù)專欄

1. Nacos介紹

2. Nacos注冊(cè)中心實(shí)現(xiàn)原理分析

2.1 Nacos架構(gòu)圖

2.2 注冊(cè)中心的原理

3. Nacos源碼分析

3.1 Nacos服務(wù)注冊(cè)

3.2 Nacos服務(wù)發(fā)現(xiàn)

今天來(lái)分享一下Nacos注冊(cè)中心的底層原理,從服務(wù)注冊(cè)到服務(wù)發(fā)現(xiàn),非常細(xì)致

1. Nacos介紹

再講Nacos之前,先來(lái)講一下服務(wù)注冊(cè)和發(fā)現(xiàn)。我們知道,現(xiàn)在微服務(wù)架構(gòu)是目前開(kāi)發(fā)的一個(gè)趨勢(shì)。服務(wù)消費(fèi)者要去調(diào)用多個(gè)服務(wù)提供者組成的集群。這里需要做到以下幾點(diǎn):

服務(wù)消費(fèi)者需要在本地配置文件中維護(hù)服務(wù)提供者集群的每個(gè)節(jié)點(diǎn)的請(qǐng)求地址。

服務(wù)提供者集群中如果某個(gè)節(jié)點(diǎn)宕機(jī),服務(wù)消費(fèi)者的本地配置中需要同步刪除這個(gè)節(jié)點(diǎn)的請(qǐng)求地址,防止請(qǐng)求發(fā)送到已經(jīng)宕機(jī)的節(jié)點(diǎn)上造成請(qǐng)求失敗。

因此需要引入服務(wù)注冊(cè)中心,它具有以下幾個(gè)功能:

服務(wù)地址的管理。

服務(wù)注冊(cè)。

服務(wù)動(dòng)態(tài)感知。

而Nacos致力于解決微服務(wù)中的統(tǒng)一配置,服務(wù)注冊(cè)和發(fā)現(xiàn)等問(wèn)題。Nacos集成了注冊(cè)中心和配置中心。其相關(guān)特性包括:

1.服務(wù)發(fā)現(xiàn)和服務(wù)健康監(jiān)測(cè)

Nacos支持基于DNS和RPC的服務(wù)發(fā)現(xiàn),即服務(wù)消費(fèi)者可以使用DNS或者HTTP的方式來(lái)查找和發(fā)現(xiàn)服務(wù)。Nacos提供對(duì)服務(wù)的實(shí)時(shí)的健康檢查,阻止向不健康的主機(jī)或者服務(wù)實(shí)例發(fā)送請(qǐng)求。Nacos支持傳輸層(Ping/TCP)、應(yīng)用層(HTTP、Mysql)的健康檢查。

2.動(dòng)態(tài)配置服務(wù)

動(dòng)態(tài)配置服務(wù)可以以中心化、外部化和動(dòng)態(tài)化的方式管理所有環(huán)境的應(yīng)用配置和服務(wù)配置。

3.動(dòng)態(tài)DNS服務(wù)

支持權(quán)重路由,讓開(kāi)發(fā)者更容易的實(shí)現(xiàn)中間層的負(fù)載均衡、更靈活的路由策略、流量控制以及DNS解析服務(wù)。

4.服務(wù)和元數(shù)據(jù)管理

Nacos允許開(kāi)發(fā)者從微服務(wù)平臺(tái)建設(shè)的視角來(lái)管理數(shù)據(jù)中心的所有服務(wù)和元數(shù)據(jù)。如:服務(wù)的生命周期、靜態(tài)依賴分析、服務(wù)的健康狀態(tài)、服務(wù)的流量管理、路由和安全策略等。

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

項(xiàng)目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

視頻教程:https://doc.iocoder.cn/video/

2. Nacos注冊(cè)中心實(shí)現(xiàn)原理分析

2.1 Nacos架構(gòu)圖

以下是Nacos的架構(gòu)圖:

2c11b3ce-4629-11ed-96c9-dac502259ad0.png

其中分為這么幾個(gè)模塊:

Provider APP:服務(wù)提供者。

Consumer APP:服務(wù)消費(fèi)者。

Name Server:通過(guò)Virtual IP或者DNS的方式實(shí)現(xiàn)Nacos高可用集群的服務(wù)路由。

Nacos Server:Nacos服務(wù)提供者。

OpenAPI:功能訪問(wèn)入口。

Config Service、Naming Service:Nacos提供的配置服務(wù)、名字服務(wù)模塊。

Consistency Protocol:一致性協(xié)議,用來(lái)實(shí)現(xiàn)Nacos集群節(jié)點(diǎn)的數(shù)據(jù)同步,使用Raft算法實(shí)現(xiàn)。

其中包含:

Nacos Console :Nacos控制臺(tái)。

小總結(jié):

服務(wù)提供者通過(guò)VIP(Virtual IP)訪問(wèn)Nacos Server高可用集群,基于OpenAPI完成服務(wù)的注冊(cè)和服務(wù)的查詢。

Nacos Server的底層則通過(guò)數(shù)據(jù)一致性算法(Raft)來(lái)完成節(jié)點(diǎn)的數(shù)據(jù)同步。

2.2 注冊(cè)中心的原理

這里對(duì)其原理做一個(gè)大致的介紹,在后文則從源碼角度進(jìn)行分析。

首先,服務(wù)注冊(cè)的功能體現(xiàn)在:

服務(wù)實(shí)例啟動(dòng)時(shí)注冊(cè)到服務(wù)注冊(cè)表、關(guān)閉時(shí)則注銷(xiāo)(服務(wù)注冊(cè))。

服務(wù)消費(fèi)者可以通過(guò)查詢服務(wù)注冊(cè)表來(lái)獲得可用的實(shí)例(服務(wù)發(fā)現(xiàn))。

服務(wù)注冊(cè)中心需要調(diào)用服務(wù)實(shí)例的健康檢查API來(lái)驗(yàn)證其是否可以正確的處理請(qǐng)求(健康檢查)。

Nacos服務(wù)注冊(cè)和發(fā)現(xiàn)的實(shí)現(xiàn)原理的圖如下:

2cdc302c-4629-11ed-96c9-dac502259ad0.png

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

項(xiàng)目地址:https://gitee.com/zhijiantianya/yudao-cloud

視頻教程:https://doc.iocoder.cn/video/

3. Nacos源碼分析

前提(在本地或者虛機(jī)上先啟動(dòng)好Nacos) 這一部分從2個(gè)角度來(lái)講Nacos是如何實(shí)現(xiàn)的:

服務(wù)注冊(cè)。

服務(wù)發(fā)現(xiàn)

3.1 Nacos服務(wù)注冊(cè)

首先看下一個(gè)包:spring-cloud-commons

2d3f4450-4629-11ed-96c9-dac502259ad0.png

這個(gè)ServiceRegistry接口是SpringCloud提供的服務(wù)注冊(cè)的標(biāo)準(zhǔn),集成到SpringCloud中實(shí)現(xiàn)服務(wù)注冊(cè)的組件,都需要實(shí)現(xiàn)這個(gè)接口。來(lái)看下它的結(jié)構(gòu):

publicinterfaceServiceRegistry{
voidregister(Rregistration);

voidderegister(Rregistration);

voidclose();

voidsetStatus(Rregistration,Stringstatus);

TgetStatus(Rregistration);
}

那么對(duì)于Nacos而言,該接口的實(shí)現(xiàn)類是NacosServiceRegistry,該類在這個(gè)pom包下:

2d84a036-4629-11ed-96c9-dac502259ad0.png

再回過(guò)頭來(lái)看spring-cloud-commons包:

2db4fd58-4629-11ed-96c9-dac502259ad0.png

spring.factories主要是包含了自動(dòng)裝配的配置信息,如圖:

2e939162-4629-11ed-96c9-dac502259ad0.png

在我之前的文章里我有提到過(guò),在spring.factories中配置EnableAutoConfiguration的內(nèi)容后,項(xiàng)目在啟動(dòng)的時(shí)候,會(huì)導(dǎo)入相應(yīng)的自動(dòng)配置類,那么也就允許對(duì)該類的相關(guān)屬性進(jìn)行一個(gè)自動(dòng)裝配。那么顯然,在這里導(dǎo)入了AutoServiceRegistrationAutoConfiguration這個(gè)類,而這個(gè)類顧名思義是服務(wù)注冊(cè)相關(guān)的配置類。

該類的完整代碼如下:

@Configuration(
proxyBeanMethods=false
)
@Import({AutoServiceRegistrationConfiguration.class})
@ConditionalOnProperty(
value={"spring.cloud.service-registry.auto-registration.enabled"},
matchIfMissing=true
)
publicclassAutoServiceRegistrationAutoConfiguration{
@Autowired(
required=false
)
privateAutoServiceRegistrationautoServiceRegistration;
@Autowired
privateAutoServiceRegistrationPropertiesproperties;

publicAutoServiceRegistrationAutoConfiguration(){
}

@PostConstruct
protectedvoidinit(){
if(this.autoServiceRegistration==null&&this.properties.isFailFast()){
thrownewIllegalStateException("AutoServiceRegistrationhasbeenrequested,butthereisnoAutoServiceRegistrationbean");
}
}
}

這里做一個(gè)分析,AutoServiceRegistrationAutoConfiguration中注入了AutoServiceRegistration實(shí)例,該類的關(guān)系圖如下:

2f94a7a4-4629-11ed-96c9-dac502259ad0.png

我們先來(lái)看一下這個(gè)抽象類AbstractAutoServiceRegistration:

publicabstractclassAbstractAutoServiceRegistrationimplementsAutoServiceRegistration,
ApplicationContextAware,
ApplicationListener{
publicvoidonApplicationEvent(WebServerInitializedEventevent){
this.bind(event);
}
}

這里實(shí)現(xiàn)了ApplicationListener接口,并且傳入了WebServerInitializedEvent作為泛型,啥意思嘞,意思是:

NacosAutoServiceRegistration監(jiān)聽(tīng)WebServerInitializedEvent事件。

也就是WebServer初始化完成后,會(huì)調(diào)用對(duì)應(yīng)的事件綁定方法,調(diào)用onApplicationEvent(),該方法最終調(diào)用NacosServiceRegistry的register()方法(NacosServiceRegistry實(shí)現(xiàn)了Spring的一個(gè)服務(wù)注冊(cè)標(biāo)準(zhǔn)接口)。

對(duì)于register()方法,主要調(diào)用的是Nacos Client SDK中的NamingService下的registerInstance()方法完成服務(wù)的注冊(cè)。

publicvoidregister(Registrationregistration){
if(StringUtils.isEmpty(registration.getServiceId())){
log.warn("Noservicetoregisterfornacosclient...");
}else{
StringserviceId=registration.getServiceId();
Stringgroup=this.nacosDiscoveryProperties.getGroup();
Instanceinstance=this.getNacosInstanceFromRegistration(registration);

try{
this.namingService.registerInstance(serviceId,group,instance);
log.info("nacosregistry,{}{}{}:{}registerfinished",newObject[]{group,serviceId,instance.getIp(),instance.getPort()});
}catch(Exceptionvar6){
log.error("nacosregistry,{}registerfailed...{},",newObject[]{serviceId,registration.toString(),var6});
ReflectionUtils.rethrowRuntimeException(var6);
}

}
}

publicvoidregisterInstance(StringserviceName,StringgroupName,Instanceinstance)throwsNacosException{
if(instance.isEphemeral()){
BeatInfobeatInfo=newBeatInfo();
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName,groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
longinstanceInterval=instance.getInstanceHeartBeatInterval();
beatInfo.setPeriod(instanceInterval==0L?DEFAULT_HEART_BEAT_INTERVAL:instanceInterval);
//1.addBeatInfo()負(fù)責(zé)創(chuàng)建心跳信息實(shí)現(xiàn)健康監(jiān)測(cè)。因?yàn)镹acosServer必須要確保注冊(cè)的服務(wù)實(shí)例是健康的。
//而心跳監(jiān)測(cè)就是服務(wù)健康監(jiān)測(cè)的一種手段。
this.beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName,groupName),beatInfo);
}
//2.registerService()實(shí)現(xiàn)服務(wù)的注冊(cè)
this.serverProxy.registerService(NamingUtils.getGroupedName(serviceName,groupName),groupName,instance);
}

再來(lái)看一下心跳監(jiān)測(cè)的方法addBeatInfo():

publicvoidaddBeatInfo(StringserviceName,BeatInfobeatInfo){
LogUtils.NAMING_LOGGER.info("[BEAT]addingbeat:{}tobeatmap.",beatInfo);
Stringkey=this.buildKey(serviceName,beatInfo.getIp(),beatInfo.getPort());
BeatInfoexistBeat=null;
if((existBeat=(BeatInfo)this.dom2Beat.remove(key))!=null){
existBeat.setStopped(true);
}

this.dom2Beat.put(key,beatInfo);
//通過(guò)schedule()方法,定時(shí)的向服務(wù)端發(fā)送一個(gè)數(shù)據(jù)包,然后啟動(dòng)一個(gè)線程不斷地檢測(cè)服務(wù)端的回應(yīng)。
//如果在指定的時(shí)間內(nèi)沒(méi)有收到服務(wù)端的回應(yīng),那么認(rèn)為服務(wù)器出現(xiàn)了故障。
//參數(shù)1:可以說(shuō)是這個(gè)實(shí)例的相關(guān)信息。
//參數(shù)2:一個(gè)long類型的時(shí)間,代表從現(xiàn)在開(kāi)始推遲執(zhí)行的時(shí)間,默認(rèn)是5000
//參數(shù)3:時(shí)間的單位,默認(rèn)是毫秒,結(jié)合5000即代表每5秒發(fā)送一次心跳數(shù)據(jù)包
this.executorService.schedule(newBeatReactor.BeatTask(beatInfo),beatInfo.getPeriod(),TimeUnit.MILLISECONDS);
MetricsMonitor.getDom2BeatSizeMonitor().set((double)this.dom2Beat.size());
}

心跳檢查如果正常,即代表這個(gè)需要注冊(cè)的服務(wù)是健康的,那么執(zhí)行下面的注冊(cè)方法registerInstance():

publicvoidregisterService(StringserviceName,StringgroupName,Instanceinstance)throwsNacosException{
LogUtils.NAMING_LOGGER.info("[REGISTER-SERVICE]{}registeringservice{}withinstance:{}",newObject[]{this.namespaceId,serviceName,instance});
Mapparams=newHashMap(9);
params.put("namespaceId",this.namespaceId);
params.put("serviceName",serviceName);
params.put("groupName",groupName);
params.put("clusterName",instance.getClusterName());
params.put("ip",instance.getIp());
params.put("port",String.valueOf(instance.getPort()));
params.put("weight",String.valueOf(instance.getWeight()));
params.put("enable",String.valueOf(instance.isEnabled()));
params.put("healthy",String.valueOf(instance.isHealthy()));
params.put("ephemeral",String.valueOf(instance.isEphemeral()));
params.put("metadata",JSON.toJSONString(instance.getMetadata()));
//這里可以看出來(lái),把上述服務(wù)實(shí)例的一些必要參數(shù)保存到一個(gè)Map中,通過(guò)OpenAPI的方式發(fā)送注冊(cè)請(qǐng)求
this.reqAPI(UtilAndComs.NACOS_URL_INSTANCE,params,(String)"POST");
}

下面直接Debug走一遍。兩個(gè)前提(這里不再展開(kāi)):

啟動(dòng)一個(gè)Nacos服務(wù)。

搞一個(gè)Maven項(xiàng)目,集成Nacos。

案例1:用Debug來(lái)理解Nacos服務(wù)注冊(cè)流程

1.項(xiàng)目初始化后,根據(jù)上文說(shuō)法,會(huì)執(zhí)行抽象類AbstractAutoServiceRegistration下面的onApplicationEvent()方法,即事件被監(jiān)聽(tīng)到。

2ff90d48-4629-11ed-96c9-dac502259ad0.png

2.作為抽象類的子類實(shí)現(xiàn)NacosAutoServiceRegistration,監(jiān)聽(tīng)到Web服務(wù)啟動(dòng)后, 開(kāi)始執(zhí)行super.register()方法。

30e323b0-4629-11ed-96c9-dac502259ad0.png

3.執(zhí)行NacosServiceRegistry下的register()方法(super),前面說(shuō)過(guò),集成到SpringCloud中實(shí)現(xiàn)服務(wù)注冊(cè)的組件,都需要實(shí)現(xiàn)ServiceRegistry這個(gè)接口,而對(duì)于Nacos而言,NacosServiceRegistry就是具體的實(shí)現(xiàn)子類。執(zhí)行注冊(cè)方法需要傳入的三個(gè)參數(shù):

實(shí)例名稱serviceId。

實(shí)例歸屬的組。

具體實(shí)例

3151cbbc-4629-11ed-96c9-dac502259ad0.png

而registerInstance()主要做兩件事:

檢查服務(wù)的健康(this.beatReactor.addBeatInfo())。

執(zhí)行服務(wù)的注冊(cè)(this.serverProxy.registerService())。

服務(wù)健康的檢查:檢查通過(guò)后,發(fā)送OpenAPI進(jìn)行服務(wù)的注冊(cè):

服務(wù)注冊(cè)小總結(jié)☆:

這里來(lái)做一個(gè)大框架式的梳理(也許前面寫(xiě)的有點(diǎn)亂,這里通過(guò)幾個(gè)問(wèn)答的形式來(lái)進(jìn)行總結(jié))

問(wèn)題1:Nacos的服務(wù)注冊(cè)為什么和spring-cloud-commons這個(gè)包扯上關(guān)系?

回答:

首先,Nacos的服務(wù)注冊(cè)肯定少不了pom包:spring-cloud-starter-alibaba-nacos-discovery吧。

這個(gè)包下面包括了spring-cloud-commons包,那么這個(gè)包有什么用?

spring-cloud-commons中有一個(gè)接口叫做ServiceRegistry,而集成到SpringCloud中實(shí)現(xiàn)服務(wù)注冊(cè)的組件,都需要實(shí)現(xiàn)這個(gè)接口。

因此對(duì)于需要注冊(cè)到Nacos上的服務(wù),也需要實(shí)現(xiàn)這個(gè)接口,那么具體的實(shí)現(xiàn)子類為NacosServiceRegistry。

問(wèn)題2:為什么我的項(xiàng)目加了這幾個(gè)依賴,服務(wù)啟動(dòng)時(shí)依舊沒(méi)有注冊(cè)到Nacos中?

回答:

本文提到過(guò),進(jìn)行Nacos服務(wù)注冊(cè)的時(shí)候,會(huì)有一個(gè)事件的監(jiān)聽(tīng)過(guò)程,而監(jiān)聽(tīng)的對(duì)象是WebServer,因此,這個(gè)項(xiàng)目需要是一個(gè)Web項(xiàng)目!

因此查看你的pom文件中是否有依賴:spring-boot-starter-web。

問(wèn)題3:除此之外,spring-cloud-commons這個(gè)包還有什么作用?

回答:

這個(gè)包下的spring.factories文件中,配置了相關(guān)的服務(wù)注冊(cè)的置類,即支持其自動(dòng)裝配。

這個(gè)配置類叫做AutoServiceRegistrationAutoConfiguration。其注入了類AutoServiceRegistration,而NacosAutoServiceRegistration是該類的一個(gè)具體實(shí)現(xiàn)。

當(dāng)WebServer初始化的時(shí)候,通過(guò)綁定的事件監(jiān)聽(tīng)器,會(huì)實(shí)現(xiàn)監(jiān)聽(tīng),執(zhí)行服務(wù)的注冊(cè)邏輯。

說(shuō)白了:

第一件事情:引入一個(gè)Spring監(jiān)聽(tīng)器,當(dāng)容器初始化后,執(zhí)行Nacos服務(wù)的注冊(cè)。

第二件事情:而Nacos服務(wù)注冊(cè)的方法的實(shí)現(xiàn),其需要實(shí)現(xiàn)的接口來(lái)自于該包下的ServiceRegistry接口。

接下來(lái)就對(duì)Nacos注冊(cè)的流程進(jìn)行一個(gè)總結(jié):

服務(wù)(項(xiàng)目)啟動(dòng)時(shí),根據(jù)spring-cloud-commons中spring.factories的配置,自動(dòng)裝配了類AutoServiceRegistrationAutoConfiguration。

AutoServiceRegistrationAutoConfiguration類中注入了類AutoServiceRegistration,其最終實(shí)現(xiàn)子類實(shí)現(xiàn)了Spring的監(jiān)聽(tīng)器。

根據(jù)監(jiān)聽(tīng)器,執(zhí)行了服務(wù)注冊(cè)方法。而這個(gè)服務(wù)注冊(cè)方法則是調(diào)用了NacosServiceRegistry的register()方法。

該方法主要調(diào)用的是Nacos Client SDK中的NamingService下的registerInstance()方法完成服務(wù)的注冊(cè)。

registerInstance()方法主要做兩件事:服務(wù)實(shí)例的健康監(jiān)測(cè)和實(shí)例的注冊(cè)。

通過(guò)schedule()方法定時(shí)的發(fā)送數(shù)據(jù)包,檢測(cè)實(shí)例的健康。

若健康監(jiān)測(cè)通過(guò),調(diào)用registerService()方法,通過(guò)OpenAPI方式執(zhí)行服務(wù)注冊(cè),其中將實(shí)例Instance的相關(guān)信息存儲(chǔ)到HashMap中。

3.2 Nacos服務(wù)發(fā)現(xiàn)

有一點(diǎn)我們需要清楚:Nacos服務(wù)的發(fā)現(xiàn)發(fā)生在什么時(shí)候。例如:微服務(wù)發(fā)生遠(yuǎn)程接口調(diào)用的時(shí)候。一般我們?cè)谑褂肙penFeign進(jìn)行遠(yuǎn)程接口調(diào)用時(shí),都需要用到對(duì)應(yīng)的微服務(wù)名稱,而這個(gè)名稱就是用來(lái)進(jìn)行服務(wù)發(fā)現(xiàn)的。

舉個(gè)例子:

@FeignClient("test-application")
publicinterfaceMyFeignService{
@RequestMapping("getInfoById")
Rinfo(@PathVariable("id")Longid);
}

接下來(lái)直接開(kāi)始講重點(diǎn),Nacos在進(jìn)行服務(wù)發(fā)現(xiàn)的時(shí)候,會(huì)調(diào)用NacosServerList類下的getServers()方法:

publicclassNacosServerListextendsAbstractServerList{
privateListgetServers(){
try{
Stringgroup=this.discoveryProperties.getGroup();
//1.通過(guò)唯一的serviceId(一般是服務(wù)名稱)和組來(lái)獲得對(duì)應(yīng)的所有實(shí)例。
Listinstances=this.discoveryProperties.namingServiceInstance().selectInstances(this.serviceId,group,true);
//2.將List轉(zhuǎn)換成List數(shù)據(jù),然后返回。
returnthis.instancesToServerList(instances);
}catch(Exceptionvar3){
thrownewIllegalStateException("Cannotgetserviceinstancesfromnacos,serviceId="+this.serviceId,var3);
}
}
}

接下來(lái)來(lái)看一下NacosNamingService.selectInstances()方法:

publicListselectInstances(StringserviceName,StringgroupName,booleanhealthy)throwsNacosException{
returnthis.selectInstances(serviceName,groupName,healthy,true);
}

該方法最終會(huì)調(diào)用到其重載方法:

publicListselectInstances(StringserviceName,StringgroupName,Listclusters,
booleanhealthy,booleansubscribe)throwsNacosException{
//保存服務(wù)實(shí)例信息的對(duì)象
ServiceInfoserviceInfo;
//如果該消費(fèi)者訂閱了這個(gè)服務(wù),那么會(huì)在本地維護(hù)一個(gè)服務(wù)列表,服務(wù)從本地獲取
if(subscribe){
serviceInfo=this.hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName,groupName),StringUtils.join(clusters,","));
}else{
//否則實(shí)例會(huì)從服務(wù)中心進(jìn)行獲取。
serviceInfo=this.hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName,groupName),StringUtils.join(clusters,","));
}

returnthis.selectInstances(serviceInfo,healthy);
}

這里應(yīng)該重點(diǎn)關(guān)注this.hostReactor這個(gè)對(duì)象,它里面比較重要的是幾個(gè)Map類型的存儲(chǔ)結(jié)構(gòu):

publicclassHostReactor{
privatestaticfinallongDEFAULT_DELAY=1000L;
privatestaticfinallongUPDATE_HOLD_INTERVAL=5000L;
//存放線程異步調(diào)用的一個(gè)回調(diào)結(jié)果
privatefinalMap>futureMap;
//本地已存在的服務(wù)列表,key是服務(wù)名稱,value是ServiceInfo
privateMapserviceInfoMap;
//待更新的實(shí)例列表
privateMapupdatingMap;
//定時(shí)任務(wù)(負(fù)責(zé)服務(wù)列表的實(shí)時(shí)更新)
privateScheduledExecutorServiceexecutor;
....
}

再看一看它的getServiceInfo()方法:

publicServiceInfogetServiceInfo(StringserviceName,Stringclusters){
LogUtils.NAMING_LOGGER.debug("failover-mode:"+this.failoverReactor.isFailoverSwitch());
Stringkey=ServiceInfo.getKey(serviceName,clusters);
if(this.failoverReactor.isFailoverSwitch()){
returnthis.failoverReactor.getService(key);
}else{
//1.先通過(guò)serverName即服務(wù)名獲得一個(gè)serviceInfo
ServiceInfoserviceObj=this.getServiceInfo0(serviceName,clusters);
//如果沒(méi)有serviceInfo,則通過(guò)傳進(jìn)來(lái)的參數(shù)new出一個(gè)新的serviceInfo對(duì)象,并且同時(shí)維護(hù)到本地Map和更新Map
//這里是serviceInfoMap和updatingMap
if(null==serviceObj){
serviceObj=newServiceInfo(serviceName,clusters);
this.serviceInfoMap.put(serviceObj.getKey(),serviceObj);
this.updatingMap.put(serviceName,newObject());
//2.updateServiceNow(),立刻去Nacos服務(wù)端拉去數(shù)據(jù)。
this.updateServiceNow(serviceName,clusters);
this.updatingMap.remove(serviceName);
}elseif(this.updatingMap.containsKey(serviceName)){
synchronized(serviceObj){
try{
serviceObj.wait(5000L);
}catch(InterruptedExceptionvar8){
LogUtils.NAMING_LOGGER.error("[getServiceInfo]serviceName:"+serviceName+",clusters:"+clusters,var8);
}
}
}
//3.定時(shí)更新實(shí)例信息
this.scheduleUpdateIfAbsent(serviceName,clusters);
//最后返回服務(wù)實(shí)例數(shù)據(jù)(前面已經(jīng)進(jìn)行了更新)
return(ServiceInfo)this.serviceInfoMap.get(serviceObj.getKey());
}
}

來(lái)看下scheduleUpdateIfAbsent()方法:

//通過(guò)心跳的方式,每10秒去更新一次數(shù)據(jù),并不是只有在調(diào)用服務(wù)的時(shí)候才會(huì)進(jìn)行更新,而是通過(guò)定時(shí)任務(wù)來(lái)異步進(jìn)行。
publicvoidscheduleUpdateIfAbsent(StringserviceName,Stringclusters){
if(this.futureMap.get(ServiceInfo.getKey(serviceName,clusters))==null){
synchronized(this.futureMap){
if(this.futureMap.get(ServiceInfo.getKey(serviceName,clusters))==null){
//創(chuàng)建一個(gè)UpdateTask的更新線程任務(wù),每10秒去異步更新集合數(shù)據(jù)
ScheduledFuturefuture=this.addTask(newHostReactor.UpdateTask(serviceName,clusters));
this.futureMap.put(ServiceInfo.getKey(serviceName,clusters),future);
}
}
}
}

案例2:用Debug來(lái)理解Nacos服務(wù)發(fā)現(xiàn)流程

1.進(jìn)行遠(yuǎn)程接口調(diào)用,觸發(fā)服務(wù)的發(fā)現(xiàn),調(diào)用NacosServerList的getServers()方法。傳入的serviceId和對(duì)應(yīng)Feign接口上的接口@FeignClient中的名稱一致。

3204ab10-4629-11ed-96c9-dac502259ad0.png

例如,我這里調(diào)用的Feign接口是:

@FeignClient("gulimall-member")
publicinterfaceMemberFeignService{
@RequestMapping("/member/member/info/{id}")
Rinfo(@PathVariable("id")Longid);
}

這里可以看出來(lái),返回的是一個(gè)Instance類型的List,對(duì)應(yīng)的服務(wù)也發(fā)現(xiàn)并返回了。

2.這里則調(diào)用了NacosNamingService的selectInstances()方法,我這里的subscribe值是true,即代表我這個(gè)消費(fèi)者直接訂閱了這個(gè)服務(wù),因此最終的信息是從本地Map中獲取,即Nacos維護(hù)了一個(gè)注冊(cè)列表。

3.再看下HostReactor的getServiceInfo()方法:最終所需要的結(jié)果是從serviceInfoMap中獲取,并且通過(guò)多個(gè)Map進(jìn)行維護(hù)服務(wù)實(shí)例,若存在數(shù)據(jù)的變化,還會(huì)通過(guò)強(qiáng)制睡眠5秒鐘的方式來(lái)等待數(shù)據(jù)的更新。

4.無(wú)論怎樣都會(huì)調(diào)用this.scheduleUpdateIfAbsent(serviceName, clusters)方法。

5.通過(guò)scheduleUpdateIfAbsent()方法定時(shí)的獲取實(shí)時(shí)的實(shí)例數(shù)據(jù),并且負(fù)責(zé)維護(hù)本地的服務(wù)注冊(cè)列表,若服務(wù)發(fā)生更新,則更新本地的服務(wù)數(shù)據(jù)。

服務(wù)發(fā)現(xiàn)小總結(jié)☆:

經(jīng)常有人說(shuō)過(guò),Nacos有個(gè)好處,就是當(dāng)一個(gè)服務(wù)掛了之后,短時(shí)間內(nèi)不會(huì)造成影響,因?yàn)橛袀€(gè)本地注冊(cè)列表,在服務(wù)不更新的情況下,服務(wù)還能夠正常的運(yùn)轉(zhuǎn),其原因如下:

Nacos的服務(wù)發(fā)現(xiàn),一般是通過(guò)訂閱的形式來(lái)獲取服務(wù)數(shù)據(jù)。

而通過(guò)訂閱的方式,則是從本地的服務(wù)注冊(cè)列表中獲?。梢岳斫鉃榫彺妫O喾?,如果不訂閱,那么服務(wù)的信息將會(huì)從Nacos服務(wù)端獲取,這時(shí)候就需要對(duì)應(yīng)的服務(wù)是健康的。(宕機(jī)就不能使用了)

在代碼設(shè)計(jì)上,通過(guò)Map來(lái)存放實(shí)例數(shù)據(jù),key為實(shí)例名稱,value為實(shí)例的相關(guān)信息數(shù)據(jù)(ServiceInfo對(duì)象)。

最后,服務(wù)發(fā)現(xiàn)的流程就是:

以調(diào)用遠(yuǎn)程接口(OpenFeign)為例,當(dāng)執(zhí)行遠(yuǎn)程調(diào)用時(shí),需要經(jīng)過(guò)服務(wù)發(fā)現(xiàn)的過(guò)程。

服務(wù)發(fā)現(xiàn)先執(zhí)行NacosServerList類中的getServers()方法,根據(jù)遠(yuǎn)程調(diào)用接口上@FeignClient中的屬性作為serviceId傳入NacosNamingService.selectInstances()方法中進(jìn)行調(diào)用。

根據(jù)subscribe的值來(lái)決定服務(wù)是從本地注冊(cè)列表中獲取還是從Nacos服務(wù)端中獲取。

以本地注冊(cè)列表為例,通過(guò)調(diào)用HostReactor.getServiceInfo()來(lái)獲取服務(wù)的信息(serviceInfo),Nacos本地注冊(cè)列表由3個(gè)Map來(lái)共同維護(hù)。

本地Map–>serviceInfoMap,

更新Map–>updatingMap

異步更新結(jié)果Map–>futureMap,

最終的結(jié)果從serviceInfoMap當(dāng)中獲取。

HostReactor類中的getServiceInfo()方法通過(guò)this.scheduleUpdateIfAbsent() 方法和updateServiceNow()方法實(shí)現(xiàn)服務(wù)的定時(shí)更新和立刻更新。

而對(duì)于scheduleUpdateIfAbsent()方法,則通過(guò)線程池來(lái)進(jìn)行異步的更新,將回調(diào)的結(jié)果(Future)保存到futureMap中,并且發(fā)生提交線程任務(wù)時(shí),還負(fù)責(zé)更新本地注冊(cè)列表中的數(shù)據(jù)。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • HTTP
    +關(guān)注

    關(guān)注

    0

    文章

    530

    瀏覽量

    34435
  • DNS
    DNS
    +關(guān)注

    關(guān)注

    0

    文章

    227

    瀏覽量

    20869
  • RPC
    RPC
    +關(guān)注

    關(guān)注

    0

    文章

    113

    瀏覽量

    12102
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    890

    瀏覽量

    28878

原文標(biāo)題:Nacos 為什么這么強(qiáng)?

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    Nacos是什么?Nacos配置管理技巧你知道嗎

    Nacos 是阿里巴巴今年7月份開(kāi)源的項(xiàng)目,如其名, Naming Configuration Service ,專注于服務(wù)發(fā)現(xiàn)和配置管理領(lǐng)域。本系列文章,將從 5W1H(What、Where
    的頭像 發(fā)表于 10-29 08:53 ?1.5w次閱讀

    Nacos的概念和功能

    1、Nacos簡(jiǎn)介 Nacos的概念和功能 Nacos是一個(gè)面向微服務(wù)架構(gòu)的動(dòng)態(tài)服務(wù)發(fā)現(xiàn)、配置管
    的頭像 發(fā)表于 09-25 11:02 ?3578次閱讀

    #硬聲創(chuàng)作季 【Nacos源碼】Nacos服務(wù)注冊(cè)發(fā)現(xiàn)源碼深度剖析

    云計(jì)算源碼nacos
    Mr_haohao
    發(fā)布于 :2022年09月14日 07:10:33

    支持Dubbo生態(tài)發(fā)展,阿里巴巴啟動(dòng)新的開(kāi)源項(xiàng)目 Nacos

    或者協(xié)議做, 同時(shí)在云上,在服務(wù)發(fā)現(xiàn)場(chǎng)景中,注冊(cè)中心更關(guān)注的是可用性而不是數(shù)據(jù)一致性,所以Nacos會(huì)首推DNS-based Servcie
    發(fā)表于 07-05 17:35

    構(gòu)建ARM64版本nacos docker鏡像

    在適配過(guò)程中有大量合作伙伴用到nacos且采用容器化部署,dockerhub未提供官方鏡像,因此需要在鯤鵬服務(wù)器自定義構(gòu)建。構(gòu)建前提:Docker已部署構(gòu)建步驟:1、下載包含構(gòu)建所需的腳本下載完成
    發(fā)表于 06-16 14:29

    還在為 Eureka 閉源擔(dān)心?Nacos 來(lái)了

    , 啟動(dòng) server 即可核心的服務(wù)注冊(cè) & 發(fā)現(xiàn),配置管理的 Demo API 也非常簡(jiǎn)單和直白: Nacos 的文檔 (https:
    發(fā)表于 08-07 15:13 ?318次閱讀

    Nacos發(fā)布0.5.0版本,輕松玩轉(zhuǎn)動(dòng)態(tài) DNS 服務(wù)

    。Eureka和Zookeeper等注冊(cè)中心,其實(shí)也支持持久化的實(shí)例注冊(cè),而Nacos只是將這種持久化的注冊(cè)設(shè)成了默認(rèn)的行為。
    發(fā)表于 12-05 16:22 ?154次閱讀

    服務(wù)配置中心實(shí)戰(zhàn):Spring + MyBatis + Druid + Nacos

    在 結(jié)合場(chǎng)景談服務(wù)發(fā)現(xiàn)和配置 中我們講述了 Nacos 配置中心的三個(gè)典型的應(yīng)用場(chǎng)景,包括如何在 Spring Boot 中使用 Nacos
    發(fā)表于 12-29 17:09 ?1272次閱讀
    微<b class='flag-5'>服務(wù)</b>配置<b class='flag-5'>中心</b>實(shí)戰(zhàn):Spring + MyBatis + Druid + <b class='flag-5'>Nacos</b>

    Nacos服務(wù)地址動(dòng)態(tài)感知原理

    Nacos Server:Nacos服務(wù)提供者,里面包含的Open API是功能訪問(wèn)入口,Conig Service、Naming Service 是Nacos提供的配置
    的頭像 發(fā)表于 09-26 10:40 ?2236次閱讀

    華為云CSE 關(guān)鍵特性,支持托管Nacos注冊(cè)配置中心

    華為云CSE關(guān)鍵特性,支持托管Nacos注冊(cè)配置中心 什么是Nacos Nacos是?Dynamic Naming and Configur
    的頭像 發(fā)表于 12-29 16:23 ?1453次閱讀
    華為云CSE 關(guān)鍵特性,支持托管<b class='flag-5'>Nacos</b><b class='flag-5'>注冊(cè)</b>配置<b class='flag-5'>中心</b>

    華為云微服務(wù)引擎0停機(jī)遷移Nacos?它是這樣做的

    dubbo-servicecomb接入CSE需要投入的成本高,且社區(qū)dubbo-servicecomb未投入人力維護(hù),可能遇到很多適配問(wèn)題。 ? 僅想使用CSE的治理能力,配置中心仍然使用Nacos,或者后期微服務(wù)整改后
    的頭像 發(fā)表于 12-29 20:01 ?1197次閱讀

    Nacos服務(wù)基本概念和核心能力以及實(shí)現(xiàn)原理

    服務(wù)發(fā)現(xiàn)是指使用一個(gè)注冊(cè)中心來(lái)記錄分布式系統(tǒng)中的全部服務(wù)的信息,以便其他服務(wù)能夠快速的找到這些已
    的頭像 發(fā)表于 05-17 17:51 ?2078次閱讀
    <b class='flag-5'>Nacos</b><b class='flag-5'>服務(wù)</b>基本概念和核心能力以及實(shí)現(xiàn)原理

    Nacos、OpenFeign、Ribbon組件協(xié)調(diào)工作的原理

    ? ? Nacos 如何進(jìn)行服務(wù)自動(dòng)注冊(cè)? Ribbon OpenFeign 總結(jié) 前幾天有個(gè)大兄弟問(wèn)了我一個(gè)問(wèn)題,注冊(cè)中心要集成Sprin
    的頭像 發(fā)表于 05-22 10:46 ?1475次閱讀
    <b class='flag-5'>Nacos</b>、OpenFeign、Ribbon組件協(xié)調(diào)工作的原理

    Nacos實(shí)現(xiàn)原理:SpringCloud集成Nacos的實(shí)現(xiàn)過(guò)程

    Nacos服務(wù)提供者,里面包含的Open API是功能訪問(wèn)入口,Conig Service、Naming Service 是Nacos提供的配置服務(wù)、命名
    發(fā)表于 10-09 16:08 ?1691次閱讀
    <b class='flag-5'>Nacos</b>實(shí)現(xiàn)原理:SpringCloud集成<b class='flag-5'>Nacos</b>的實(shí)現(xiàn)過(guò)程

    springcloud alibaba 五大組件

    發(fā)現(xiàn)組件(Nacos): Nacos是一個(gè)用于實(shí)現(xiàn)服務(wù)注冊(cè)發(fā)現(xiàn)的組件。它提供了一個(gè)簡(jiǎn)單易用的
    的頭像 發(fā)表于 12-03 16:30 ?2.2w次閱讀