摘要:服務(wù)續(xù)約在服務(wù)注冊完成之后,服務(wù)提供者需要維護(hù)一個(gè)心跳來告知注冊中心服務(wù)實(shí)例處于正常運(yùn)行狀態(tài)中,防止注冊中心將正常的服務(wù)實(shí)例剔除出注冊中心。
Spring Cloud Eureka 目錄
前言
構(gòu)建服務(wù)注冊中心
服務(wù)注冊與發(fā)現(xiàn)
Eureka的基礎(chǔ)架構(gòu)
Eureka的服務(wù)治理機(jī)制
Eureka的配置
代碼地址
前言 服務(wù)治理?隨著微服務(wù)應(yīng)用的不斷增加,靜態(tài)配置會越來越難以維護(hù),并且隨著業(yè)務(wù)的不斷發(fā)展,集群規(guī)模、服務(wù)位置、服務(wù)命名都會發(fā)生變化,手動(dòng)維護(hù)的方式極易發(fā)生錯(cuò)誤或是命名沖突問題。因此需要服務(wù)治理框架對微服務(wù)實(shí)例進(jìn)行管理,服務(wù)治理是微服務(wù)架構(gòu)中最核心的功能和模塊,主要用來各個(gè)微服務(wù)實(shí)例的自動(dòng)化注冊和發(fā)現(xiàn)。
服務(wù)注冊?在服務(wù)治理框架中,通常都會有一個(gè)服務(wù)注冊中心。
?每一個(gè)微服務(wù)實(shí)例向注冊中心登記自己提供的服務(wù),將主機(jī)、端口號、版本號、通信協(xié)議等一些信息告知注冊中心。
?注冊中心按服務(wù)名分類組織服務(wù)清單。
?服務(wù)注冊中心需要以心跳的方式監(jiān)測服務(wù)清單中的服務(wù)是否可用,如果不可用,需要將不可用的服務(wù)實(shí)例進(jìn)行剔除。
服務(wù)發(fā)現(xiàn)?服務(wù)間的調(diào)用通過向服務(wù)名發(fā)起請求調(diào)用實(shí)現(xiàn)。 服務(wù)調(diào)用方在調(diào)用提供方的接口時(shí),并不知道提供方的具體地址。
?服務(wù)調(diào)用方需要從注冊中心獲取所有服務(wù)的實(shí)例清單,才可以實(shí)現(xiàn)對具體服務(wù)實(shí)例的訪問。
?服務(wù)調(diào)用方在發(fā)起調(diào)用時(shí),會以某種策略取出一個(gè)具體的服務(wù)實(shí)例進(jìn)行服務(wù)調(diào)用(客戶端負(fù)載均衡)。
?在實(shí)際的環(huán)境中,為了提供性能,并不會采用每次都向服務(wù)注冊中心獲取服務(wù)的方式進(jìn)行服務(wù)的調(diào)用,并且不同的應(yīng)用場景在緩存和服務(wù)剔除等機(jī)制上可以采用不同的實(shí)現(xiàn)策略。
Netflix Eureka?Spring Cloud Eureka采用Netflix Eureka來實(shí)現(xiàn)服務(wù)注冊與發(fā)現(xiàn),包含客戶端和服務(wù)端組件。
?支持高可用配置。
?依托于強(qiáng)一致性提供良好的服務(wù)實(shí)例可用性。
?服務(wù)注冊中心之間可以通過異步模式互相復(fù)制各自的狀態(tài)。
?主要用于服務(wù)的注冊和發(fā)現(xiàn)。
?客戶端可以通過注解和參數(shù)配置的方式實(shí)現(xiàn)注冊與發(fā)現(xiàn)。
?Eureka客戶端向注冊中心注冊自身提供的服務(wù)并周期性地發(fā)送心跳來更新它的服務(wù)租約。
?Eureka客戶端從服務(wù)端查詢當(dāng)前注冊的服務(wù)信息并把它們緩存到本地并周期性的刷新服務(wù)狀態(tài)。
構(gòu)建服務(wù)注冊中心 構(gòu)建注冊中心(位于spring-eureka-server的Module下)package cn.sh.eureka.server; //代碼位于該包下
1.準(zhǔn)備pom.xml
org.springframework.boot spring-boot-starter-parent 2.0.3.RELEASE org.springframework.cloud spring-cloud-starter-eureka-server 1.2.7.RELEASE org.springframework.cloud spring-cloud-dependencies Finchley.RELEASE pom import
2.準(zhǔn)備配置文件application.properties
#端口號 server.port=9000 eureka.instance.hostname=localhost #禁止注冊中心注冊自己 eureka.client.register-with-eureka=false #禁止注冊中心搜索服務(wù) eureka.client.fetch-registry=false eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
3.編寫注冊中心代碼(@EnableEurekaServer)
package cn.sh.eureka.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * @author sh * @EnableEurekaServer 啟動(dòng)一個(gè)注冊中心 */ @EnableEurekaServer @SpringBootApplication public class EurekaServer { public static void main(String[] args) { SpringApplication.run(EurekaServer.class, args); } }構(gòu)建微服務(wù)應(yīng)用(服務(wù)提供者)
1.準(zhǔn)備pom.xml
org.springframework.boot spring-boot-starter-parent 2.0.3.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka 1.2.5.RELEASE org.springframework.cloud spring-cloud-dependencies Finchley.RELEASE pom import
2.準(zhǔn)備配置文件application.properties
#端口號 server.port=8000 #服務(wù)名稱 spring.application.name=produce-service #服務(wù)注冊中心地址 eureka.client.serviceUrl.defaultZone=http://localhost:9000/eureka/
3.編寫produce-service服務(wù)的代碼
代碼位于spring-cloud-eureka-client模塊下
package cn.sh.eureka.produce; //代碼包位置高可用注冊中心搭建
?Eureka Server的高可用實(shí)際上就是講自己作為服務(wù)向其他服務(wù)注冊中心注冊自己,這樣可以形成一組互相注冊的服務(wù)注冊中心,以實(shí)現(xiàn)服務(wù)清單的互相同步,達(dá)到高可用的效果。
?相關(guān)實(shí)現(xiàn)在spring-cloud-eureka-server模塊下,注意在host配置文件中配置peer1和peer2的轉(zhuǎn)換
?將代碼通過maven編譯成jar包
?通過java命令啟動(dòng)兩個(gè)注冊中心
java -jar spring-cloud-eureka-server-1.0.jar --spring.profiles.active=peer1 java -jar spring-cloud-eureka-server-1.0.jar --spring.profiles.active=peer2
?啟動(dòng)之后,會發(fā)現(xiàn)在注冊中心peer1的面板上的available-replicas中出現(xiàn)http://peer2:9002/eureka/,同樣,在注冊中心peer2面板上的available-replicas中出現(xiàn)http://peer1:9001/eureka/
eureka.instance.prefer-ip-address=true, 使用IP地址的方式指定注冊中心的地址,默認(rèn)false,以主機(jī)名定義注冊中心地址
服務(wù)發(fā)現(xiàn)與消費(fèi)?服務(wù)消費(fèi)者的主要目標(biāo)是發(fā)現(xiàn)和消費(fèi)服務(wù)。其中服務(wù)發(fā)現(xiàn)由Eureka客戶端完成,服務(wù)消費(fèi)由Ribbon完成。
Ribbon簡單介紹?Ribbon是一個(gè)基于HTTP和TCP的客戶端負(fù)載均衡器,它可以通過在客戶端中配置的ribbonServerList服務(wù)實(shí)例列表去輪詢訪問以達(dá)到負(fù)載均衡的作用。
?Ribbon與Eureka結(jié)合使用時(shí),ribbonServerList服務(wù)實(shí)例列表會被DiscoveryEnabledNIWSServerList重寫,擴(kuò)展成從Eureka注冊中心中獲取服務(wù)列表。
?Ribbon與Eureka結(jié)合使用時(shí),采用NIWSDiscoveryPing取代IPing,它將職責(zé)委托給Eureka來確定服務(wù)實(shí)例是否啟動(dòng)。
構(gòu)建消費(fèi)者1.pom.xml
?增加對Ribbon的支持
org.springframework.cloud spring-cloud-starter-ribbon 1.2.7.RELEASE
2.相關(guān)代碼在spring-cloud-eureka-consumer模塊下
Eureka詳解 基礎(chǔ)結(jié)構(gòu)服務(wù)注冊中心:Eureka服務(wù)端,提供服務(wù)注冊和發(fā)現(xiàn)的功能
服務(wù)提供者:提供服務(wù)的應(yīng)用,將自己提供的服務(wù)注冊到Eureka,供其他應(yīng)用發(fā)現(xiàn)
服務(wù)消費(fèi)者:消費(fèi)者從注冊中心發(fā)現(xiàn)服務(wù)列表,然后調(diào)用對應(yīng)的服務(wù)(Ribbon或Feign)
備注: 一般一個(gè)應(yīng)用既是服務(wù)提供者也是服務(wù)消費(fèi)者。
服務(wù)治理機(jī)制?Eureka服務(wù)體系圖
?服務(wù)提供者會以Rest請求的方式注冊到注冊中心上,在請求過程中會攜帶自身的一些元數(shù)據(jù)信息。注冊中心在收到請求后,會將元數(shù)據(jù)信息保存到一個(gè)雙層Map結(jié)構(gòu)中,外層的key是服務(wù)名稱,內(nèi)層的key是具體的服務(wù)實(shí)例名稱。
?eureka.client.register-with-eureka,如果該參數(shù)的值等于false,不會進(jìn)行注冊。
?如果兩個(gè)服務(wù)注冊在兩個(gè)不同的注冊中心上,兩個(gè)注冊中心互相注冊成為服務(wù)(集群),此時(shí),當(dāng)服務(wù)提供者向其中一個(gè)注冊中心發(fā)起請求時(shí),該注冊中心會將請求準(zhǔn)發(fā)給集群中的其他注冊中心,從而實(shí)現(xiàn)注冊中心之間的服務(wù)同步。
?由于服務(wù)同步的存在,服務(wù)提供者的信息可以在任意一臺注冊中心上獲取。
?在服務(wù)注冊完成之后,服務(wù)提供者需要維護(hù)一個(gè)心跳來告知注冊中心服務(wù)實(shí)例處于正常運(yùn)行狀態(tài)中,防止注冊中心將正常的服務(wù)實(shí)例剔除出注冊中心。上述操作就成為服務(wù)續(xù)約。
屬性 | 含義 |
---|---|
eureka.instance.lease-renewal-interval-in-seconds | 服務(wù)續(xù)約任務(wù)調(diào)用的間隔時(shí)間,默認(rèn)時(shí)間30s |
eureka.instance.lease-expiration-duration-in-seconds | 服務(wù)失效時(shí)間(表示注冊中心至上一次收到客戶端的心跳之后,等待下一次心跳的超時(shí)時(shí)間,在這個(gè)時(shí)間內(nèi)若沒收到下一次心跳,則將移除該客戶端實(shí)例),默認(rèn)90s |
?啟動(dòng)服務(wù)消費(fèi)者時(shí),服務(wù)消費(fèi)者會向注冊中心發(fā)起一個(gè)Rest請求,來獲取注冊中心維護(hù)的服務(wù)實(shí)例清單。但是為了提高性能,注冊中心會維護(hù)一份只讀的服務(wù)清單返回給客戶端,該緩存的服務(wù)清單會每隔30s刷新一次。
?eureka.client.fetch-registry,如果該參數(shù)被設(shè)置為false,無法向注冊中心獲取服務(wù)清單。
?eureka.client.registry-fetch-interval-seconds,緩存清單的刷新時(shí)間,默認(rèn)30s。
?服務(wù)消費(fèi)者獲得服務(wù)清單后,可以根據(jù)服務(wù)名獲取具體服務(wù)實(shí)例列表(元數(shù)據(jù)信息),根據(jù)自己的策略選擇具體的服務(wù)實(shí)例進(jìn)行調(diào)用。
?Eureka有Region和Zone的概念,一個(gè)Region中會有多個(gè)Zone,每個(gè)客戶端都需要注冊到一個(gè)Zone中,所以客戶端對應(yīng)一個(gè)Region和一個(gè)Zone。在服務(wù)進(jìn)行調(diào)用時(shí),優(yōu)先訪問同一個(gè)Zone中的服務(wù)提供方,若訪問不到,再訪問其他Zone。
?當(dāng)服務(wù)實(shí)例正常關(guān)閉時(shí),服務(wù)實(shí)例會發(fā)送一個(gè)服務(wù)下線的Rest請求給注冊中心。注冊中心在收到請求后,會將該服務(wù)實(shí)例的狀態(tài)置為DOWN,并且將下線時(shí)間廣播出去。
服務(wù)注冊中心?當(dāng)服務(wù)實(shí)例未正常下線時(shí)(內(nèi)存溢出、網(wǎng)絡(luò)故障),服務(wù)注冊中心未能收到服務(wù)下線的Rest請求。注冊中心在啟動(dòng)時(shí)會創(chuàng)建一個(gè)定時(shí)任務(wù),默認(rèn)每隔一段時(shí)間(60s)將當(dāng)前清單中超時(shí)(服務(wù)失效時(shí)間,默認(rèn)90s)沒有續(xù)約的服務(wù)進(jìn)行剔除。
?注冊中心在運(yùn)行期間,會統(tǒng)計(jì)心跳失敗比例在15分鐘內(nèi)是否低于85%,如果出現(xiàn)低于的情況,注冊中心會將當(dāng)前服務(wù)實(shí)例的注冊信息保護(hù)起來,讓這些實(shí)例不會過期。但是,在保護(hù)期時(shí)間內(nèi),如果實(shí)例出現(xiàn)問題,那么服務(wù)調(diào)用者很容易拿到該實(shí)例調(diào)用失敗,所以服務(wù)調(diào)用者必須要有容錯(cuò)機(jī)制(請求重試、斷路由器等)。
?eureka.server.enable-self-preservation,如果該值設(shè)置為false,則不啟用自我保護(hù)機(jī)制,默認(rèn)值為true
源碼分析?分析源碼,可以以Eureka客戶端和Eureka服務(wù)端作為切入點(diǎn)。
Eureka客戶端在應(yīng)用獲取服務(wù)列表和向注冊中心注冊成為服務(wù)時(shí)只做了兩件事:
在啟動(dòng)類上使用@EnableEurekaClient或者@EnableDiscoveryClient注解
在application.properties中添加eureka.client.serviceUrl.defaultZone
@EurekaDiscoveryClient?該注解的主要作用是用來開啟一個(gè)DiscoveryClient的實(shí)例。
org.springframework.cloud.client.discovery.DiscoveryClient類?類圖以后補(bǔ)充
類 | 作用 |
---|---|
org.springframework.cloud.client.discovery.DiscoveryClient | Spring Cloud的接口,定義了發(fā)現(xiàn)服務(wù)的常用抽象方法,這樣做的好處是可以屏蔽服務(wù)治理的細(xì)節(jié),可以方便的切換不同的服務(wù)治理框架,不需要改動(dòng)程序代碼,只需要添加一些針對服務(wù)治理框架的配置 |
com.netflix.appinfo.InstanceInfo.LookupService | 定義了Eureka發(fā)現(xiàn)服務(wù)的抽象方法 |
com.netflix.discovery.EurekaClient | 定義了Eureka發(fā)現(xiàn)服務(wù)的抽象方法,繼承LookupService |
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient | Spring中的DiscoveryClient接口的實(shí)現(xiàn),對Eureka發(fā)現(xiàn)服務(wù)的封裝,實(shí)現(xiàn)EurekaClient接口,Eureka接口繼承LookupService接口 |
com.netflix.discovery.DiscoveryClient | 實(shí)現(xiàn)EurekaClient接口,真正的服務(wù)發(fā)現(xiàn)實(shí)現(xiàn)類 |
通過頭部的注釋信息,我們可以得到以下信息:
1.DiscoveryClient類主要用于幫助客戶端和注冊中心互相協(xié)作。
2.Eureka客戶端主要負(fù)責(zé)的任務(wù):
向注冊中心注冊服務(wù)實(shí)例
向注冊中心進(jìn)行服務(wù)租約
當(dāng)服務(wù)關(guān)閉期間,向注冊中心取消租約
查詢注冊中心的服務(wù)實(shí)例列表
3.Eureka客戶端需要配置一個(gè)注冊中心的URL列表
Eureka客戶端注冊中心URL列表進(jìn)行配置?關(guān)鍵類:com.netflix.discovery.endpoint.EndpointUtils
?關(guān)鍵方法:Map
/** * Get the list of all eureka service urls from properties file for the eureka client to talk to. * * @param clientConfig the clientConfig to use * @param instanceZone The zone in which the client resides * @param preferSameZone true if we have to prefer the same zone as the client, false otherwise * @return an (ordered) map of zone -> list of urls mappings, with the preferred zone first in iteration order */ public static Map> getServiceUrlsMapFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) { Map > orderedUrls = new LinkedHashMap<>(); String region = getRegion(clientConfig); String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion()); if (availZones == null || availZones.length == 0) { availZones = new String[1]; availZones[0] = DEFAULT_ZONE; } logger.debug("The availability zone for the given region {} are {}", region, availZones); int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones); String zone = availZones[myZoneOffset]; List serviceUrls = clientConfig.getEurekaServerServiceUrls(zone); if (serviceUrls != null) { orderedUrls.put(zone, serviceUrls); } int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1); while (currentOffset != myZoneOffset) { zone = availZones[currentOffset]; serviceUrls = clientConfig.getEurekaServerServiceUrls(zone); if (serviceUrls != null) { orderedUrls.put(zone, serviceUrls); } if (currentOffset == (availZones.length - 1)) { currentOffset = 0; } else { currentOffset++; } } if (orderedUrls.size() < 1) { throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!"); } return orderedUrls; }
1.加載Region、Zone
?通過getRegion函數(shù),從配置文件中讀取一個(gè)Region返回,所以一個(gè)微服務(wù)應(yīng)用只能屬于一個(gè)Region,如果不進(jìn)行配置,默認(rèn)值default,eureka.client.region該屬性可以配置Region
?通過getAvailabilityZones函數(shù),如果沒有為Region配置Zone,默認(rèn)值是defaultZone,因此注冊中心URL列表的默認(rèn)配置參數(shù)為eureka.client.serviceUrl.defaultZone。eureka.client.availability-zone.
2.加載serviceUrls
?按照一定的方法以此加載每個(gè)Zone中的urls,存放在一個(gè)Map
?當(dāng)使用Ribbon來調(diào)用服務(wù)時(shí),Ribbon的默認(rèn)策略是優(yōu)先訪問和客戶端處于同一個(gè)Zone的微服務(wù)實(shí)例。
?在獲取到客戶端配置的serviceUrls之后,就可以進(jìn)行服務(wù)的注冊,詳情請看下面。
向注冊中心注冊服務(wù)(服務(wù)注冊)?關(guān)鍵類:com.netflix.discovery.DiscoveryClient
?關(guān)鍵方法:void initScheduledTasks(),DiscoveryClient的構(gòu)造函數(shù)會對此方法進(jìn)行調(diào)用
?該方法主要用來啟用定時(shí)任務(wù),主要包括獲取服務(wù)Task、服務(wù)注冊、服務(wù)續(xù)約(心跳),本次先著重看服務(wù)注冊的邏輯
/** * Initializes all scheduled tasks. */ private void initScheduledTasks() { //此處是該方法的其他邏輯 if (clientConfig.shouldRegisterWithEureka()) { int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs); // Heartbeat timer // InstanceInfo replicator instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { @Override public String getId() { return "statusChangeListener"; } @Override public void notify(StatusChangeEvent statusChangeEvent) { if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) { // log at warn level if DOWN was involved logger.warn("Saw local status change event {}", statusChangeEvent); } else { logger.info("Saw local status change event {}", statusChangeEvent); } instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { applicationInfoManager.registerStatusChangeListener(statusChangeListener); } instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); } else { logger.info("Not registering with Eureka server per configuration"); } }
1.判斷是否允許客戶端向注冊中心注冊
?eureka.client.register-with-eureka 該參數(shù)要設(shè)置成true,默認(rèn)值為true
?如果上述參數(shù)值為true,則執(zhí)行注冊任務(wù)
2.新建InstanceInfoReplicator,利用其來進(jìn)行注冊
?該類實(shí)現(xiàn)了Runnable接口,觀察run方法,在進(jìn)行注冊時(shí)實(shí)際調(diào)用的是com.netflix.discovery.DiscoveryClient類的register()方法,下面是run方法的源碼
public void run() { try { discoveryClient.refreshInstanceInfo(); Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); if (dirtyTimestamp != null) { discoveryClient.register(); instanceInfo.unsetIsDirty(dirtyTimestamp); } } catch (Throwable t) { logger.warn("There was a problem with the instance info replicator", t); } finally { Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } }
3.看一下DiscoveryClient.register()做了什么
?在該方法中,通過發(fā)送Rest請求進(jìn)行注冊操作,在發(fā)送請求時(shí)會傳入一個(gè)InstanceInfo,該對象就是客戶端的元數(shù)據(jù)信息。
服務(wù)獲取?服務(wù)獲取任務(wù)也是在initScheduledTasks方法中啟動(dòng),由于客戶端需要不斷獲取服務(wù)端維護(hù)的服務(wù)實(shí)例清單,因此該任務(wù)會以定時(shí)任務(wù)啟動(dòng),在啟動(dòng)過程會首先獲取配置文件中eureka.client.registry-fetch-interval-seconds參數(shù)配置的值(默認(rèn)是30s),
然后根據(jù)配置的參數(shù)每30s(按照實(shí)際配置的值)獲取一次服務(wù)。實(shí)際獲取服務(wù)列表的時(shí)候也是發(fā)送Rest請求。
/** * Initializes all scheduled tasks. */ private void initScheduledTasks() { if (clientConfig.shouldFetchRegistry()) { // registry cache refresh timer int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); } }服務(wù)續(xù)約
?服務(wù)在注冊到注冊中心后,需要維持一個(gè)心跳去續(xù)約,防止被剔除,因此服務(wù)續(xù)約和服務(wù)注冊是成對存在,在同一個(gè)if條件里。首先會獲取配置文件參數(shù)中eureka.instance.lease-renewal-interval-in-seconds的值,該值默認(rèn)30s,隨后啟動(dòng)定時(shí)任務(wù),每隔30s(根據(jù)實(shí)際配置的值)向注冊中心發(fā)一次Rest請求,表明自己還活著。
/** * Initializes all scheduled tasks. */ private void initScheduledTasks() { if (clientConfig.shouldRegisterWithEureka()) { int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs); // Heartbeat timer scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); } }服務(wù)注冊中心處理
?注冊中心處理Rest請求的類位于com.netflix.eureka.resources包下
?比如com.netflix.eureka.resources.ApplicationResource類中的addInstance()方法主要用來處理客戶端的注冊事件。
?注冊中心在收到客戶端發(fā)送注冊請求以后,會首先對客戶端的信息進(jìn)行校驗(yàn),校驗(yàn)過后會調(diào)用org.springframework.cloud.netflix.eureka.server.InstanceRegistry的register()方法來進(jìn)行服務(wù)注冊。
?register()方法會調(diào)用publishEvent()方法將服務(wù)注冊的事件傳播出去,緊接著調(diào)用父類com.netflix.eureka.registry.AbstractInstanceRegistry中的register()實(shí)現(xiàn),該方法會將InstanceInfo中的元數(shù)據(jù)信息保存到一個(gè)ConcurrentHashMap中。
?該HashMap有兩層數(shù)據(jù)結(jié)構(gòu),正如我們之前所說,第一層的Key存儲服務(wù)名(InstanceInfo中的appName屬性),第二層Key存儲服務(wù)實(shí)例名稱(InstanceInfo中的instanceId屬性)
配置詳解Eureka客戶端的配置主要有以下兩個(gè)方面:
服務(wù)注冊相關(guān)的配置信息,包括注冊中心的地址、服務(wù)獲取的時(shí)間間隔、可用區(qū)域(Zone)等;
服務(wù)實(shí)例相關(guān)的配置信息,包括服務(wù)實(shí)例的名稱、IP地址、端口號、健康檢查路徑等。
?客戶端的配置類可以參考o(jì)rg.springframework.cloud.netflix.eureka.EurekaClientConfigBean
?服務(wù)端的配置類可以參考o(jì)rg.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean
服務(wù)注冊相關(guān)的配置?這些配置信息都以eureka.client作為前綴
指定注冊中心?指定注冊中心主要靠eureka.client.serviceUrl參數(shù),該參數(shù)的配置值值存儲在HashMap中,并且會有一組默認(rèn)值,默認(rèn)值的Key是defaultZone,value為http://localhost:8761/eureka/。
?當(dāng)構(gòu)建了高可用的服務(wù)注冊中心集群時(shí),參數(shù)的value可以配置多個(gè)注冊中心(通過,分隔)。
?為了注冊中心的安全考慮,需要為服務(wù)注冊中心加入安全校驗(yàn)。因此客戶端在配置注冊中心serviceUrl中時(shí)需要加入相應(yīng)的安全校驗(yàn)信息。比如http://
參數(shù)名 | 說明 | 默認(rèn)值 |
---|---|---|
enabled | 啟用Eureka客戶端 | true |
registryFetchIntervalSeconds | 從服務(wù)注冊中心獲取注冊信息(服務(wù)實(shí)例清單)的間隔時(shí)間,單位s | 30 |
instanceInfoReplicationIntervalSeconds | 更新實(shí)例信息的變化到Eureka服務(wù)端的間隔時(shí)間,單位s | 30 |
initialInstanceInfoReplicationIntervalSeconds | 最初更新實(shí)例信息到Eureka服務(wù)端的間隔時(shí)間,單位s | 40 |
eurekaServiceUrlPollIntervalSeconds | 輪詢Eureka服務(wù)端地址更改的間隔時(shí)間,單位s。當(dāng)我們與Spring Cloud Config配合,動(dòng)態(tài)刷新Eureka的serviceUrl地址時(shí)需要關(guān)注該參數(shù) | 300 |
eurekaServerReadTimeoutSeconds | 讀取注冊中心信息的超時(shí)時(shí)間,單位s | 8 |
eurekaServerConnectTimeoutSeconds | 連接注冊中心的超時(shí)時(shí)間,單位s | 5 |
eurekaServerTotalConnections | 從Eureka客戶端到所有Eureka服務(wù)端的連接總數(shù) | 200 |
eurekaServerTotalConnectionsPerHost | 從Eureka客戶端到每個(gè)Eureka服務(wù)端主機(jī)的連接總數(shù) | 50 |
eurekaConnectionIdleTimeoutSeconds | Eureka服務(wù)端連接的空閑關(guān)閉時(shí)間 | 30 |
heartbeatExecutorThreadPoolSize | 心跳連接池的初始化線程數(shù) | 2 |
heartbeatExecutorExponentialBackOffBound | 心跳超時(shí)重試延遲時(shí)間的最大乘數(shù)值 | 10 |
cacheRefreshExecutorThreadPoolSize | 緩存刷新線程池的初始化線程池?cái)?shù) | 2 |
cacheRefreshExecutorExponentialBackOffBound | 緩存刷新重試延遲時(shí)間的最大乘數(shù)值 | 10 |
useDnsForFetchingServiceUrls | 使用DNS來獲取Eureka服務(wù)端的serviceUrl | false |
registerWithEureka | 是否要將自身的實(shí)例信息注冊到Eureka服務(wù)端 | true |
preferSameZoneEureka | 是否偏好使用處于相同Zone的Eureka服務(wù)端 | true |
filterOnlyUpInstances | 獲取實(shí)例時(shí)是否過濾,僅保留UP狀態(tài)的實(shí)例 | true |
fetchRegistry | 是否從Eureka服務(wù)端獲取注冊信息 | true |
?該配置可以參考o(jì)rg.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean類,這些配置都以eureka.instance開頭
元數(shù)據(jù)?在EurekaInstanceConfigBean類中有一大部分內(nèi)容是對服務(wù)實(shí)例元數(shù)據(jù)的配置,它是Eureka客戶端在向注冊中心發(fā)送注冊請求時(shí),用來描述自身服務(wù)信息的對象。
?在使用Spring Cloud Eureka時(shí),所有的配置信息都是通過EurekaInstanceConfigBean對象進(jìn)行加載,但真正進(jìn)行服務(wù)注冊的時(shí)候,會包裝成com.netflix.appinfo.InstanceInfo對象發(fā)送給Eureka服務(wù)端。
?在InstanceInfo類中,Map
?在配置文件可以通過eureka.instance.
?實(shí)例名,即InstanceInfo中的instanceId,它是區(qū)別同一服務(wù)中不同實(shí)例的唯一標(biāo)識。
?在原生的Netflix Eureka中,實(shí)例名稱采用主機(jī)名作為默認(rèn)值,這樣的弊端就是無法在同一主機(jī)上啟動(dòng)多個(gè)相同的服務(wù)實(shí)例。
?在Spring Cloud Eureka中,對實(shí)例名的默認(rèn)值做了更合理的擴(kuò)展,它采用了${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
?實(shí)例名的命名規(guī)則,可以通過eureka.instance.instance-id進(jìn)行配置,比如在客戶端進(jìn)行負(fù)載均衡調(diào)用時(shí),需要啟動(dòng)多個(gè)端口進(jìn)行調(diào)試,此時(shí)就可以修改eureka.instance.instance-id=${spring.application.name}:${random.int}
端點(diǎn)配置?在InstanceInfo中,可以看到一些URL的配置信息,如:homePageUrl、statusPageUrl、healthCheckUrl等,它分別代表了應(yīng)用主頁的URL、狀態(tài)頁的URL、健康檢查的URL。
?狀態(tài)頁和健康檢查的URL默認(rèn)使用了spring-boot-actuator模塊提供的/actuator/info和/actuator/health端點(diǎn)(2.0版本之前不帶/actuator前綴)。
?為了服務(wù)的正常運(yùn)作,必須確保/actuator/health端點(diǎn)是一個(gè)能夠被注冊中心可以訪問的地址,否則注冊中心不會根據(jù)應(yīng)用的健康檢查來更新狀態(tài)(只有eureka.client.healthcheck.enabled=true時(shí)才會以該端點(diǎn)對服務(wù)進(jìn)行健康監(jiān)測,否則會以客戶端心跳的方式。)。
?/info端點(diǎn)如果不正確,會導(dǎo)致在注冊中心點(diǎn)擊服務(wù)實(shí)例鏈接,會無法獲取到服務(wù)實(shí)例提供的信息接口。
?在大多數(shù)情況下,并不需要修改URL的配置,但是不排除特殊情況需要對這些URL做配置。比如:為應(yīng)用設(shè)置context-path,這時(shí),所有spring-boot-actuator模塊的監(jiān)控端點(diǎn)都會增加一個(gè)前綴。示例配置如下:
server.servlet.context-path=/produce eureka.instance.status-page-url-path=${server.servlet.context-path}/info eureka.instance.health-check-url-path=${server.servlet.context-path}/health
?eureka.instance.status-page-url-path和eureka.instance.health-check-url-path兩個(gè)參數(shù)均使用相對路徑來配置。
?eureka.instance.status-page-url和eureka.instance.health-check-url兩個(gè)配置參數(shù)使用絕對路徑進(jìn)行配置。對比上面可以發(fā)現(xiàn),如果使用相對路徑后面參數(shù)后綴都會跟著path
健康檢測?默認(rèn)情況下,Eureka中各個(gè)服務(wù)實(shí)例的健康檢測并不是通過spring-cloud-actuator模塊的/actuator/health節(jié)點(diǎn),而是依靠客戶端的心跳來保持服務(wù)的存活。
?默認(rèn)的客戶端心跳方式無法保證客戶端提供正常的服務(wù)。比如微服務(wù)一般會有依賴的外部資源(如數(shù)據(jù)庫、緩存、消息代理等),假如與這些外部資源無法聯(lián)通,但是客戶端心跳依舊存在,這就會導(dǎo)致調(diào)用出現(xiàn)問題。
?使用spring-boot-actuator模塊的/actuator/health端點(diǎn),只需要兩部曲:
在pom.xml文件中引入spring-boot-actuator依賴
在配置文件中加入eureka.client.healthcheck.enabled=true
其他配置參數(shù) | 說明 | 默認(rèn)值 |
---|---|---|
preferIpAddress | 是否優(yōu)先以使用IP地址作為主機(jī)名的標(biāo)識 | false |
leaseRenewalIntervalInSeconds | 客戶端向注冊中心發(fā)送心跳的間隔時(shí)間,單位s | 30 |
leaseExpirationDurationInSeconds | 服務(wù)端在收到最后一次心跳后的等待時(shí)間上限,單位s。超過該時(shí)間會對服務(wù)進(jìn)行剔除。 | 90 |
nonSecurePort | 非安全的通信端口號 | 80 |
securePort | 安全的通信端口號 443 | |
nonSecurePortEnabled | 是否啟用非安全的通信端口號 | true |
securePortEnabled | 是否啟用安全的通信端口號 | true |
appname | 服務(wù)名,默認(rèn)會取spring.application.name的值 | unknown |
hostname | 主機(jī)名 | 根據(jù)計(jì)算操作系統(tǒng)的主機(jī)名進(jìn)行取值 |
spring-cloud-eureka代碼
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/76494.html
摘要:下一篇介紹基于的服務(wù)注冊與調(diào)用。服務(wù)提供者工程配置這里服務(wù)提供者是使用之前進(jìn)階教程第三篇整合連接池以及監(jiān)控改造而來,這里一樣的部分就不再重復(fù)說明,下面將說明新增的部分。 Spring Cloud簡介 Spring Cloud是一個(gè)基于Spring Boot實(shí)現(xiàn)的云應(yīng)用開發(fā)工具,它為基于JVM的云應(yīng)用開發(fā)中涉及的配置管理、服務(wù)發(fā)現(xiàn)、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分...
摘要:筆者也是初學(xué)者,本文從創(chuàng)建項(xiàng)目工程開始,一步一步開始講解如何創(chuàng)建服務(wù)端和客戶端,一起學(xué)習(xí),共同進(jìn)步。下面我們使用工具創(chuàng)建相關(guān)項(xiàng)目。配置其中兩個(gè)屬性表明這個(gè)應(yīng)用是端,而不是端。至此,端和端已經(jīng)部署成功。 前言 spring cloud為互聯(lián)企業(yè)構(gòu)建微服務(wù)提供了一整套的技術(shù)組件,其中Eureka是Spring Cloud體系中的核心。Netfix不是一個(gè)技術(shù)概念,它原本是國外一個(gè)視頻網(wǎng)站的...
摘要:授權(quán)框架使第三方應(yīng)用程序來獲取對服務(wù)的有限訪問機(jī)會。無論是通過編排資源所有者和服務(wù)之間的交互批準(zhǔn)的資源所有者,或通過允許第三方應(yīng)用程序來獲取自己的訪問權(quán)限。 SpringCloud打造微服務(wù)平臺--概覽 簡述 SpringCloud是什么 Spring Boot和SpringCloud是什么關(guān)系 Spring Boot是Spring的一套快速WEB開發(fā)的腳手架,可建立獨(dú)立的Sprin...
摘要:本例中介紹如何使用來完成服務(wù)調(diào)用并實(shí)現(xiàn)負(fù)載均衡。即,對于注冊中心而言,生產(chǎn)者和調(diào)用者都是端。文件配置如下在文件中,我們將應(yīng)用命名為,端口為,表示注冊中心地址。 前言 Ribbon是Spring Cloud體系中完成負(fù)載均衡的重要組件。Spring Cloud體系中有兩種完成服務(wù)調(diào)用的組件,一種是Ribbon+RestTemplate,另一種Feign。Feign默認(rèn)使用的也是Ribbo...
閱讀 2203·2021-11-15 11:38
閱讀 1164·2021-09-06 15:02
閱讀 3404·2021-08-27 13:12
閱讀 1372·2019-08-30 14:20
閱讀 2410·2019-08-29 15:08
閱讀 651·2019-08-29 14:08
閱讀 1736·2019-08-29 13:43
閱讀 1472·2019-08-26 12:11