java基础之----eureka
概述
最近幾年微服務(wù)火的一塌糊涂,先是從dubbo開始,之后火的是spring cloud,spring cloud整合了非常多的組件,比如注冊中心eureka,rpc遠(yuǎn)程調(diào)用組件feign,負(fù)載均衡組件ribbon,熔斷限流工具h(yuǎn)ystrix,網(wǎng)關(guān)zuul,以及配置中心,上面提到的大多數(shù)組件都是Netflix公司開源的,但是上面的很多工具多多少少都有些問題,而且有些都已經(jīng)停止更新了,比如我們這篇文章談的eureka,只有1.0版本,2.0版本就沒有維護(hù)了,在之后呢?號稱具有地表最強(qiáng)java團(tuán)隊的阿里開源了非常多的組件,用于微服務(wù),比如注冊中心和配置中心nacos,熔斷限流工具sentinel等,所以以后的趨勢就是spring cloud alibaba,所以不得不說alibaba,就是牛逼。
本文講述主要內(nèi)容
1.eureka可以做注冊中心,他是如何處理服務(wù)注冊和服務(wù)發(fā)現(xiàn)的
2.eureka的心跳機(jī)制是如何決定一個服務(wù)下線的
3.eureka的自我保護(hù)機(jī)制是什么玩意
4.eureka為什么符合AP原則,而不是像zookeeper那樣符合CP原則
5.eureka在生產(chǎn)環(huán)境如何調(diào)優(yōu)
帶著上面的幾個問題,來學(xué)習(xí)eureka
eureka作為注冊中心,基本原理
eureka是如何實(shí)現(xiàn)服務(wù)注冊和服務(wù)發(fā)現(xiàn)的,eureka中會維護(hù)一個服務(wù)注冊表,這個服務(wù)注冊表是放在eureka服務(wù)的內(nèi)存中的,如果eureka是一個集群部署的,那eureka在每個節(jié)點(diǎn)中都維護(hù)一套服務(wù)注冊表,并且盡量同步成一致(這里為什么不說所有的服務(wù)注冊表都一樣,這個就是問題4要講的內(nèi)容,eureka符合AP原則),然后每個客戶端會從eureka服務(wù)端定時拉取這個服務(wù)注冊表放到自己的內(nèi)存中,每次拉取更新一下,然后客戶端如果要調(diào)用別的服務(wù)直接從自己本地的服務(wù)注冊表中就可以找到要調(diào)用服務(wù)的地址和端口信息。
ok,有了上面的大致理解,來看一下eureka底層到底是如何做的,看下圖。
上圖就是eureka內(nèi)部數(shù)據(jù)結(jié)構(gòu)以及交互過程,下面具體講一下(上圖中線程定時檢測下線的服務(wù)的時間默認(rèn)不是90s,而是60s,寫錯了)
從上圖可以看出,eureka的存儲結(jié)構(gòu)中并不是只有一個,而是分兩塊,一塊是數(shù)據(jù)存儲層,一塊是二級緩存層,那為什么要分兩塊呢?直接搞一個存儲結(jié)構(gòu)不更加簡單,這么做的原因其實(shí)是為了讀寫分離,ReadOnlyCacheMap這個其實(shí)是一個ConcurrentHashMap,主要的作用就是負(fù)責(zé)客戶端獲取服務(wù)注冊表的時候返回給客戶端。服務(wù)注冊其實(shí)是不和他交互的,而是直接和registy交互。如果不設(shè)計成這樣,那就要加鎖,這里就涉及到讀鎖和寫鎖,會導(dǎo)致系統(tǒng)設(shè)計復(fù)雜和降低讀寫的性能。
服務(wù)注冊過程
保存服務(wù)信息,將服務(wù)信息保存到 registry 中;
更新隊列,將此事件添加到更新隊列中,供 Eureka Client 增量同步服務(wù)信息使用。
清空二級緩存,即 readWriteCacheMap,用于保證數(shù)據(jù)的一致性。
更新閾值,供剔除服務(wù)使用。
同步服務(wù)信息,將此事件同步至其他的 Eureka Server 節(jié)點(diǎn)。
上面的幾個過程大家看了之后估計會有下面幾個疑問?
第2步:將事件更新到隊列中,上圖中沒有畫隊列啊,那隊列在哪里?
上圖是從別的文章盜的,其實(shí)在eureka中是有維護(hù)一個隊列的,這個隊列是當(dāng)客戶端進(jìn)行同步注冊表的時候才會用到,下面講到服務(wù)發(fā)現(xiàn)的時候會講到這個隊列的作用。
第3步:清空readWriteCacheMap,為什么要清空,而不是直接把服務(wù)注冊信息同步到readWriteCacheMap?
readWriteCacheMap,這個緩存其實(shí)不是ConcurrentHashMap,而是google開發(fā)的guava,這玩意就是一個內(nèi)存中的key-value緩存,里面包括key的失效機(jī)制等。其實(shí)我也不明白eureka為什么這么設(shè)計,直接一下全部清空里面的key-value,其實(shí)對于服務(wù)注冊來說,完全可以不處理這個,當(dāng)有客戶端來同步這個服務(wù)的時候,再從registy中同步到readWriteCacheMap中就可以了。
第4步:更新閾值,這個閾值又是什么?
這個說起來就復(fù)雜了,這是eureka的一個自我保護(hù)機(jī)制,可以同步配置來配置是否開啟。
自我保護(hù)閾值 = 服務(wù)總數(shù) * 每分鐘續(xù)約數(shù) * 自我保護(hù)閾值因子。
每分鐘續(xù)約數(shù) =(60S/ 客戶端續(xù)約間隔)
最后自我保護(hù)閾值的計算公式為:
自我保護(hù)閾值 = 服務(wù)總數(shù) * (60S/ 客戶端續(xù)約間隔) * 自我保護(hù)閾值因子。
舉例:如果有 100 個服務(wù),續(xù)約間隔是 30S,自我保護(hù)閾值 0.85。
自我保護(hù)閾值 =100 * 60 / 30 * 0.85 = 170。
如果上一分鐘的續(xù)約數(shù) =180>170,則說明大量服務(wù)可用,是服務(wù)問題,進(jìn)入剔除流程;
如果上一分鐘的續(xù)約數(shù) =150<170,則說明大量服務(wù)不可用,是注冊中心自己的問題,進(jìn)入自我保護(hù)模式,不進(jìn)入剔除流程。
這個自我保護(hù)機(jī)制怎么說呢,如果真是因為網(wǎng)絡(luò)問題,導(dǎo)致很多節(jié)點(diǎn)的心跳接收都失敗,這時候eureka不進(jìn)行服務(wù)剔除是可以理解的,這是一種很好的保護(hù)系統(tǒng)的方法,但是如果不是因為網(wǎng)絡(luò)問題,而是確實(shí)要下線那么多服務(wù),這個就很雞肋了。
服務(wù)下線檢測
在上圖中,有一個單獨(dú)的線程定時會去遍歷registy,找出實(shí)例的lastUpdateTimestamp,判斷這個時間和當(dāng)前時間的間隔,比如超過60s,就認(rèn)為該服務(wù)異常了,但是這時候并不能隨便下線哦,上面已經(jīng)講了eureka的自我保護(hù)機(jī)制,這里其實(shí)就是在每個檢測周期內(nèi)看一下需要線下的節(jié)點(diǎn)數(shù)量有沒有超過閾值,如果沒有超過,就直接下線,如果超過閾值就會啟動自我保護(hù)機(jī)制。
服務(wù)獲取過程
上圖很詳細(xì)的畫出了獲取服務(wù)的過程,我就不敘述了,這里還記的上面那個問題嗎就是隊列的作用?
從上圖可以看出,這里從registy同步到readWriteCacheMap的過程有兩種方法,一個是增量,一個是全量,全量就不說就是直接全部弄過去就可以了,這里說一下增量,因為每次注冊服務(wù)都會把服務(wù)也放到隊列中一份,所以如果要增量同步的時候,直接從隊列中獲取就可以了。
eureka符合AP原則
eureka的設(shè)計,就是集群中的每個節(jié)點(diǎn)都是對等的,保證每個節(jié)點(diǎn)的數(shù)據(jù)最終一致性,但是并不能保證強(qiáng)一致性,舉個例子,服務(wù)在注冊的時候是向eureka集群中的一個節(jié)點(diǎn)注冊的,之后這個節(jié)點(diǎn)再異步的把注冊的信息同步到其他節(jié)點(diǎn),如果還沒有同步到其他節(jié)點(diǎn),這個節(jié)點(diǎn)就掛了,那么這個注冊信息就丟失了,但是這并不妨礙eureka的使用,但是這就不能保證一致性了,就是剩下的節(jié)點(diǎn)的注冊表中其實(shí)是缺失的,那為什么說是最終一致性呢?注冊失敗的服務(wù)會重新選擇一個節(jié)點(diǎn)注冊,保證最后可以注冊成功,然后同步到所有的節(jié)點(diǎn)(這里注意:是這一個節(jié)點(diǎn)向所有的節(jié)點(diǎn)同步,而不存在傳遞,比如A服務(wù)->B服務(wù) ,A服務(wù)->C服務(wù),但是不能A服務(wù)->B服務(wù),B服務(wù)->C服務(wù)),這就是最終一致性,這樣設(shè)計還是可以接受的。
zookeeper也是可以作為注冊中心的,但是zookeeper的設(shè)計是符合CP原則的,zookeeper可以保證一致性,但是不能保證可用性,因為zookeeper中有一個主節(jié)點(diǎn),如果主節(jié)點(diǎn)掛了,那就會啟動選主過程,這個過程要持續(xù)幾十秒,在這幾十秒中整個集群是不可以使用的,由于都是集群設(shè)計方式,那分區(qū)容錯性自然都滿足。
eureka生產(chǎn)環(huán)境調(diào)優(yōu)
由于eureka的默認(rèn)的一些參數(shù)設(shè)置的有些不合理,如果不做任何處理,直接部署到生產(chǎn)環(huán)境,可能會有很大的問題,首先第一個問題,就是服務(wù)發(fā)現(xiàn)時間過長的問題,比如一個服務(wù)注冊成功,首先從ReadWriteCacheMap定時同步到ReadOnlyCacheMap,這個定時任務(wù)的默認(rèn)間隔是30s,而客戶端拉去服務(wù)注冊表默認(rèn)也是30拉去一次,這樣一來一個服務(wù)從注冊到發(fā)現(xiàn)都要1分鐘,時間太長,可以將這個時間調(diào)小一點(diǎn),比如調(diào)整成2s。
還有eureka中的線程定時檢測心跳異常的那個定時任務(wù),時間間隔也太長,默認(rèn)是60s執(zhí)行一次,那如果一個服務(wù)異常了要等到這么久才可以發(fā)現(xiàn),太恐怖了。下面就是具體一些可以調(diào)整的參數(shù)。
#掃描失效服務(wù)的間隔時間(單位毫秒,默認(rèn)是60*1000)即60秒,這個是默認(rèn)時間,改成6秒
eureka.server.eviction-interval-timer-in-ms= 6000
#從eureka服務(wù)器注冊表中獲取注冊信息的時間間隔(s),默認(rèn)為30秒
eureka.client.registry-fetch-interval-seconds=2
具體可以調(diào)優(yōu)的參數(shù)參考:https://www.cnblogs.com/xmzJava/p/11359636.html
本文參考:
微服務(wù)注冊中心 Eureka 架構(gòu)深入解讀
總結(jié)
以上是生活随笔為你收集整理的java基础之----eureka的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。