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