EUREKA原理总结
Eureka高可用架構(gòu)
https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance
?
?
上圖中主要的名稱說(shuō)明:
- Register:EurekaClient注冊(cè)(Http請(qǐng)求)到EurekaServer,EurekaClient會(huì)發(fā)送自己元數(shù)據(jù)(ip,port,主頁(yè)等),EurekaServer會(huì)將其添加到服務(wù)注冊(cè)列表Map里
- Renew:EurekaClient默認(rèn)每30秒發(fā)送心跳(timer定時(shí)任務(wù),發(fā)送Http請(qǐng)求)到EurekaServer續(xù)約,向EurekaServer證明其活性,EurekaServer將EurekaClient心跳中的時(shí)間戳參數(shù)與已有服務(wù)列表中對(duì)應(yīng)的該服務(wù)的時(shí)間戳進(jìn)行比較,不相等就更新對(duì)應(yīng)的服務(wù)列表;如果EurekaServer 90秒都沒(méi)收到某個(gè)EurekaClient的續(xù)約,并且沒(méi)有進(jìn)入保護(hù)模式,就會(huì)將該服務(wù)從服務(wù)列表將其剔除(Eviction)
- Get Registry:EurekaClient默認(rèn)每30秒從EurekaServer獲取服務(wù)注冊(cè)列表,并且會(huì)與自身本地已經(jīng)緩存過(guò)的服務(wù)列表進(jìn)行比較合并,有點(diǎn)像本地倉(cāng)庫(kù)從git倉(cāng)庫(kù)進(jìn)行g(shù)it pull
- Cancel:EurekaClient服務(wù)的下線
- Make Remote Call:EurekaClient服務(wù)間進(jìn)行遠(yuǎn)程調(diào)用,比如通過(guò)RestTemplate+Ribbon或Fegin
- us-east-1c:美國(guó)東海岸EurekaServer
- Replicate:EurekaServer集群節(jié)點(diǎn)之間數(shù)據(jù)(主要是服務(wù)注冊(cè)列表信息)同步
?
?
?
Eureka源碼?
1. 是純正的 servlet 應(yīng)用,需構(gòu)建成war包部署
2. 使用了 Jersey 框架(如注解@POST、@Consumers)實(shí)現(xiàn)自身的 RESTful HTTP接口
3. peer之間的同步與服務(wù)的注冊(cè)全部通過(guò) HTTP 協(xié)議實(shí)現(xiàn)
4. 定時(shí)任務(wù)(發(fā)送心跳續(xù)約、定時(shí)清理過(guò)期服務(wù)Eviction、節(jié)點(diǎn)同步等)通過(guò) JDK 自帶的 Timer 實(shí)現(xiàn)
5. 內(nèi)存緩存使用Google的guava包實(shí)現(xiàn)
EurekaServer初始化
由于是Servlet應(yīng)用,所以Eureka需要通過(guò)servlet的相關(guān)監(jiān)聽(tīng)器 ServletContextListener 嵌入到 Servlet 的生命周期中。EurekaBootStrap 類實(shí)現(xiàn)了該接口,在servlet標(biāo)準(zhǔn)的contextInitialized()方法中完成了EurekaServer初始化工作:
/*** Initializes Eureka, including syncing up with other Eureka peers and publishing the registry.** @see* javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)*/@Overridepublic void contextInitialized(ServletContextEvent event) {try {initEurekaEnvironment();initEurekaServerContext();ServletContext sc = event.getServletContext();sc.setAttribute(EurekaServerContext.class.getName(), serverContext);} catch (Throwable e) {logger.error("Cannot bootstrap eureka server :", e);throw new RuntimeException("Cannot bootstrap eureka server :", e);}}EurekaBootStrap實(shí)現(xiàn)了ServletContextListener接口,重寫(xiě)了contextInitialized方法;
initEurekaEnvironment主要做的是讀取配置信息,設(shè)置eureka的數(shù)據(jù)中心datacenter和環(huán)境environment;
initEurekaServerContext主要是初始化servlet代碼如下:
protected void initEurekaServerContext() throws Exception {...//省略代碼 PeerAwareInstanceRegistry registry;if (isAws(applicationInfoManager.getInfo())) {...//省略代碼,如果是AWS的代碼} else {registry = new PeerAwareInstanceRegistryImpl(eurekaServerConfig,eurekaClient.getEurekaClientConfig(),serverCodecs,eurekaClient);}PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(registry,eurekaServerConfig,eurekaClient.getEurekaClientConfig(),serverCodecs,applicationInfoManager);}
?
與Spring Cloud結(jié)合的膠水代碼
Eureka是一個(gè)純正的Servlet應(yīng)用,而Spring Boot使用的是嵌入式Tomcat, 因此就需要一定的膠水代碼讓Eureka跑在Embedded Tomcat中。
這部分工作是在 EurekaServerBootstrap 中完成的。
與上面提到的EurekaBootStrap相比,它的代碼幾乎是直接將原生代碼copy過(guò)來(lái)的,雖然它并沒(méi)有繼承 ServletContextListener, 但是相應(yīng)的生命周期方法都還在,然后添加了@Configuration注解使之能被Spring容器感知:
原生的?EurekaBootStrap?類實(shí)現(xiàn)了標(biāo)準(zhǔn)的ServletContextListener接口,Spring Cloud的EurekaServerBootstrap類沒(méi)有實(shí)現(xiàn)servlet接口,但是保留了接口方法的完整實(shí)現(xiàn)。
在類EurekaServerInitializerConfiguration中實(shí)現(xiàn)了?ServletContextAware(拿到了tomcat的ServletContext對(duì)象)、SmartLifecycle(Spring容器初始化該bean時(shí)會(huì)調(diào)用相應(yīng)生命周期方法):
?
@Configuration @CommonsLog public class EurekaServerInitializerConfigurationimplements ServletContextAware, SmartLifecycle, Ordered { }?
在?start()?方法中:
eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);在SpringCloud中,Spring容器初始化該組件時(shí),Spring調(diào)用其生命周期方法start()從而觸發(fā)了Eureka的啟動(dòng):
@Overridepublic void start() {new Thread(new Runnable() {@Overridepublic void run() {try {eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext); // 調(diào)用 servlet 接口方法手工觸發(fā)啟動(dòng)log.info("Started Eureka Server");// ... ... }catch (Exception ex) {// Help!log.error("Could not initialize Eureka servlet context", ex);}}}).start();}?
重要的代碼入口
1. com.netflix.appinfo.InstanceInfo類封裝了服務(wù)注冊(cè)所需的全部信息
2. Eureka Client探測(cè)本機(jī)IP是通過(guò)org.springframework.cloud.commons.util.InetUtils工具類實(shí)現(xiàn)的
3. com.netflix.eureka.resources.ApplicationResource類相當(dāng)于Spring MVC中的控制器,是服務(wù)的注冊(cè)、查詢功能的代碼入口點(diǎn)
一個(gè)服務(wù)啟動(dòng)后最長(zhǎng)可能需要2分鐘時(shí)間才能被其它服務(wù)感知到
?當(dāng)一個(gè)服務(wù)A啟動(dòng)后,首先需要注冊(cè)到某個(gè)EurekaServer集群節(jié)點(diǎn)上,已經(jīng)注冊(cè)到EurekaServer上的服務(wù)B此時(shí)想要Make Remote Call服務(wù)A,最長(zhǎng)可能要等兩分鐘,這是因?yàn)?span style="color:#ff0000;">三處緩存和一處延遲:
?1.? Eureka Client對(duì)已經(jīng)獲取到的本地的注冊(cè)信息也做了30s緩存。
即服務(wù)通過(guò)eureka客戶端第一次查詢服務(wù)注冊(cè)信息會(huì)請(qǐng)求EurekaServer,然后會(huì)將請(qǐng)求返回的結(jié)果緩存,下次再調(diào)用時(shí)就不會(huì)真正向Eureka發(fā)起HTTP請(qǐng)求了,直接查詢本地的服務(wù)注冊(cè)信息
2.?負(fù)載均衡組件Ribbon也有30s緩存
Ribbon會(huì)從Eureka Client本地獲取服務(wù)列表,然后將結(jié)果緩存30s
?
3. EurekaServer對(duì)HTTP響應(yīng)做了緩存。在EurekaServer的”控制器”類 ApplicationResource的109行可以看到有一行
responseCache引用的是ResponseCache類型,該類型是一個(gè)接口,其get()方法首先會(huì)去緩存中查詢數(shù)據(jù),如果沒(méi)有則生成數(shù)據(jù)返回(即真正去查詢注冊(cè)列表),且緩存的有效時(shí)間為30s。
也就是說(shuō),客戶端拿到Eureka的響應(yīng)并不一定是即時(shí)的,大部分時(shí)候只是緩存信息。
?
?
4.?如果你并不是在Spring Cloud環(huán)境下使用這些組件(Eureka, Ribbon),你的服務(wù)一啟動(dòng)(不是啟動(dòng)完成)并不會(huì)馬上向Eureka注冊(cè),而是需要等到第一次發(fā)送心跳請(qǐng)求時(shí)才會(huì)注冊(cè)
心跳請(qǐng)求的發(fā)送間隔也是30s。(Spring Cloud對(duì)此做了修改,服務(wù)啟動(dòng)后會(huì)馬上注冊(cè))
?
Eureka保護(hù)機(jī)制
https://www.cnblogs.com/theRhyme/p/10058988.html
?
?
Eureka比Zookeeper好在哪里
Zookeeper保證CP
注冊(cè)中心需求分析及關(guān)鍵設(shè)計(jì)考量:?https://www.cnblogs.com/theRhyme/p/10130714.html
當(dāng)向注冊(cè)中心查詢服務(wù)列表時(shí),我們可以容忍注冊(cè)中心返回的是幾分鐘以前的注冊(cè)信息,但不能接受服務(wù)直接down掉不可用。
也就是說(shuō),服務(wù)注冊(cè)功能對(duì)可用性的要求要高于一致性。
但是zk會(huì)出現(xiàn)這樣一種情況,當(dāng)master節(jié)點(diǎn)因?yàn)榫W(wǎng)絡(luò)故障與其他節(jié)點(diǎn)失去聯(lián)系時(shí),剩余節(jié)點(diǎn)會(huì)重新進(jìn)行leader選舉。問(wèn)題在于,選舉leader的時(shí)間太長(zhǎng),30 ~ 120s, 且選舉期間整個(gè)zk集群都是不可用的,這就導(dǎo)致在選舉期間注冊(cè)服務(wù)癱瘓。
在云部署的環(huán)境下,因網(wǎng)絡(luò)問(wèn)題使得zk集群失去master節(jié)點(diǎn)是較大概率會(huì)發(fā)生的事,雖然服務(wù)能夠最終恢復(fù),但是漫長(zhǎng)的選舉時(shí)間導(dǎo)致的注冊(cè)長(zhǎng)期不可用是不能容忍的。
Eureka保證AP
Eureka優(yōu)先保證可用性。Eureka各個(gè)節(jié)點(diǎn)都是平等的,幾個(gè)節(jié)點(diǎn)掛掉不會(huì)影響正常節(jié)點(diǎn)的工作,剩余的節(jié)點(diǎn)依然可以提供注冊(cè)和查詢服務(wù)。
而Eureka的客戶端在向某個(gè)Eureka注冊(cè)或時(shí)如果發(fā)現(xiàn)連接失敗,則會(huì)自動(dòng)切換至其它節(jié)點(diǎn),只要有一臺(tái)Eureka還在,就能保證注冊(cè)服務(wù)可用(保證可用性),只不過(guò)查到的信息可能不是最新的(不保證強(qiáng)一致性)。
除此之外,Eureka還有一種自我保護(hù)機(jī)制,如果在15分鐘內(nèi)超過(guò)85%的節(jié)點(diǎn)都沒(méi)有正常的心跳,那么Eureka就認(rèn)為客戶端與注冊(cè)中心出現(xiàn)了網(wǎng)絡(luò)故障,此時(shí)會(huì)出現(xiàn)以下幾種情況:
1. Eureka不再?gòu)淖?cè)列表中移除因?yàn)殚L(zhǎng)時(shí)間沒(méi)收到心跳而應(yīng)該過(guò)期的服務(wù)
2. Eureka仍然能夠接受新服務(wù)的注冊(cè)和查詢請(qǐng)求,但是不會(huì)被同步到其它節(jié)點(diǎn)上(即保證當(dāng)前節(jié)點(diǎn)依然可用)
3. 當(dāng)網(wǎng)絡(luò)穩(wěn)定時(shí),當(dāng)前實(shí)例新的注冊(cè)信息會(huì)被同步到其它節(jié)點(diǎn)中
因此, Eureka可以很好的應(yīng)對(duì)因網(wǎng)絡(luò)故障導(dǎo)致部分節(jié)點(diǎn)失去聯(lián)系的情況,而不會(huì)像zookeeper那樣使整個(gè)注冊(cè)服務(wù)癱瘓。
就算Eureka節(jié)點(diǎn)一個(gè)都不可用,還有緩存的服務(wù)列表,如EurekaClient本地的服務(wù)列表。
?
?
Service Health Check
?
使用 ZooKeeper 作為服務(wù)注冊(cè)中心時(shí),服務(wù)的健康檢測(cè)常利用 ZooKeeper 的?Session 活性 Track機(jī)制?以及結(jié)合?Ephemeral ZNode的機(jī)制,簡(jiǎn)單而言,就是將服務(wù)的健康監(jiān)測(cè)綁定在了?ZooKeeper 對(duì)于 Session 的健康監(jiān)測(cè)上,或者說(shuō)綁定在TCP長(zhǎng)鏈接活性探測(cè)上了。
?
這在很多時(shí)候也會(huì)造成致命的問(wèn)題,ZK 與服務(wù)提供者機(jī)器之間的TCP長(zhǎng)鏈接活性探測(cè)正常的時(shí)候,該服務(wù)就是健康的么?答案當(dāng)然是否定的!注冊(cè)中心應(yīng)該提供更豐富的健康監(jiān)測(cè)方案,服務(wù)的健康與否的邏輯應(yīng)該開(kāi)放給服務(wù)提供方自己定義,而不是一刀切搞成了 TCP 活性檢測(cè)!
?
健康檢測(cè)的一大基本設(shè)計(jì)原則就是盡可能真實(shí)的反饋服務(wù)本身的真實(shí)健康狀態(tài),否則一個(gè)不敢被服務(wù)調(diào)用者相信的健康狀態(tài)判定結(jié)果還不如沒(méi)有健康檢測(cè)。
?
?
?
參考來(lái)源:
https://blog.csdn.net/forezp/article/details/73017664
https://blog.csdn.net/neosmith/article/details/53131023
https://yq.aliyun.com/articles/601745?spm=a2c4e.11153940.blogcont604028.19.6daf2a38OLvUBo
轉(zhuǎn)載于:https://www.cnblogs.com/theRhyme/p/10313299.html
總結(jié)
以上是生活随笔為你收集整理的EUREKA原理总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python迭代-如何对迭代器做切片操作
- 下一篇: HackerRank Lists