1、問題起源
Spring Cloud微服務架構體系中,Eureka是一個相當重要的組件,它扮演着微服務註冊中心的角色,全部的服務註冊與服務發現,都是依賴Eureka的。算法
很多初學Spring Cloud的朋友在落地公司生產環境部署時,常常會問:緩存
- Eureka Server到底要部署幾臺機器?
- 咱們的系統那麼多服務,到底會對Eureka Server產生多大的訪問壓力?
- Eureka Server能不能抗住一個大型系統的訪問壓力?
若是你也有這些疑問,彆着急!我們這就一塊兒去看看,Eureka做爲微服務註冊中心的核心原理性能優化
下面這些問題,你們先看看,有個大概印象。帶着這些問題,來看後面的內容,效果更佳!網絡
- Eureka註冊中心使用什麼樣的方式來儲存各個服務註冊時發送過來的機器地址和端口號?
- 各個服務找Eureka Server拉取註冊表的時候,是什麼樣的頻率?
- 各個服務是如何拉取註冊表的?
- 一個有幾百個服務,部署了上千臺機器的大型分佈式系統,會對Eureka Server形成多大的訪問壓力?
- Eureka Server從技術層面是如何抗住日千萬級訪問量的?
先給你們說一個基本的知識點,各個服務內的Eureka Client組件,默認狀況下,每隔30秒會發送一個請求到Eureka Server,來拉取最近有變化的服務信息數據結構
舉個例子:架構
- 庫存服務本來部署在1臺機器上,如今擴容了,部署到了3臺機器,而且均註冊到了Eureka Server上。
- 而後訂單服務的Eureka Client會每隔30秒去找Eureka Server拉取最近註冊表的變化,看看其餘服務的地址有沒有變化。
除此以外,Eureka還有一個心跳機制,各個Eureka Client每隔30秒會發送一次心跳到Eureka Server,通知人家說,哥們,我這個服務實例還活着!併發
若是某個Eureka Client很長時間沒有發送心跳給Eureka Server,那麼就說明這個服務實例已經掛了。分佈式
光看上面的文字,你們可能沒什麼印象。老規矩!我們仍是來一張圖,一塊兒來直觀的感覺一下這個過程。微服務
2、Eureka Server設計精妙的註冊表存儲結構
如今我們假設手頭有一套大型的分佈式系統,一共100個服務,每一個服務部署在20臺機器上,機器是4核8G的標準配置。高併發
也就是說,至關於你一共部署了100 * 20 = 2000個服務實例,有2000臺機器。
每臺機器上的服務實例內部都有一個Eureka Client組件,它會每隔30秒請求一次Eureka Server,拉取變化的註冊表。
此外,每一個服務實例上的Eureka Client都會每隔30秒發送一次心跳請求給Eureka Server。
那麼你們算算,Eureka Server做爲一個微服務註冊中心,每秒鐘要被請求多少次?一天要被請求多少次?
- 按標準的算法,每一個服務實例每分鐘請求2次拉取註冊表,每分鐘請求2次發送心跳
- 這樣一個服務實例每分鐘會請求4次,2000個服務實例每分鐘請求8000次
- 換算到每秒,則是8000 / 60 = 133次左右,咱們就大概估算爲Eureka Server每秒會被請求150次
- 那一天的話,就是8000 * 60 * 24 = 1152萬,也就是天天千萬級訪問量
好!通過這麼一個測算,你們是否發現這裏的奧祕了?
- 首先,對於微服務註冊中心這種組件,在一開始設計它的拉取頻率以及心跳發送頻率時,就已經考慮到了一個大型系統的各個服務請求時的壓力,每秒會承載多大的請求量。
- 因此各服務實例每隔30秒發起請求拉取變化的註冊表,以及每隔30秒發送心跳給Eureka Server,其實這個時間安排是有其用意的。
按照咱們的測算,一個上百個服務,幾千臺機器的系統,按照這樣的頻率請求Eureka Server,日請求量在千萬級,每秒的訪問量在150次左右。
即便算上其餘一些額外操做,咱們姑且就算每秒鐘請求Eureka Server在200次~300次吧。
因此經過設置一個適當的拉取註冊表以及發送心跳的頻率,能夠保證大規模系統裏對Eureka Server的請求壓力不會太大。
如今關鍵的問題來了,Eureka Server是如何保證輕鬆抗住這每秒數百次請求,天天千萬級請求的呢?
要搞清楚這個,首先得清楚Eureka Server究竟是用什麼來存儲註冊表的?三個字,看源碼!
接下來我們就一塊兒進入Eureka源碼裏一探究竟:
- 如上圖所示,圖中這個名字叫作registry的CocurrentHashMap,就是註冊表的核心結構。看完以後忍不住先讚歎一下,精妙的設計!
- 從代碼中能夠看到,Eureka Server的註冊表直接基於純內存,即在內存裏維護了一個數據結構。
- 各個服務的註冊、服務下線、服務故障,所有會在內存裏維護和更新這個註冊表。
- 各個服務每隔30秒拉取註冊表的時候,Eureka Server就是直接提供內存裏存儲的有變化的註冊表數據給他們就能夠了。
一樣,每隔30秒發起心跳時,也是在這個純內存的Map數據結構裏更新心跳時間。
一句話歸納:維護註冊表、拉取註冊表、更新心跳時間,所有發生在內存裏!這是Eureka Server很是核心的一個點。
搞清楚了這個,我們再來分析一下registry這個東西的數據結構,你們千萬別被它複雜的外表唬住了,沉下心來,一層層的分析!
- 首先,這個ConcurrentHashMap的key就是服務名稱,好比「inventory-service」,就是一個服務名稱。
- value則表明了一個服務的多個服務實例。
- 舉例:好比「inventory-service」是能夠有3個服務實例的,每一個服務實例部署在一臺機器上。
再來看看做爲value的這個Map:Map<String, Lease<InstanceInfo>>
- 這個Map的key就是服務實例的id
- value是一個叫作Lease的類,它的泛型是一個叫作InstanceInfo的東東,你可能會問,這倆又是什麼鬼?
- 首先說下InstanceInfo,其實啊,咱們見名知義,這個InstanceInfo就表明了服務實例的具體信息,好比機器的ip地址、hostname以及端口號。
- 而這個Lease,裏面則會維護每一個服務最近一次發送心跳的時間
3、Eureka Server端優秀的多級緩存機制
假設Eureka Server部署在4核8G的普通機器上,那麼基於內存來承載各個服務的請求,每秒鐘最多能夠處理多少請求呢?
- 根據以前的測試,單臺4核8G的機器,處理純內存操做,哪怕加上一些網絡的開銷,每秒處理幾百請求也是輕鬆加愉快的。
- 並且Eureka Server爲了不同時讀寫內存數據結構形成的併發衝突問題,還採用了多級緩存機制來進一步提高服務請求的響應速度。
- 在拉取註冊表的時候:
- 首先從ReadOnlyCacheMap裏查緩存的註冊表。
- 若沒有,就找ReadWriteCacheMap裏緩存的註冊表。
- 若是尚未,就從內存中獲取實際的註冊表數據。
- 在註冊表發生變動的時候:
- 會在內存中更新變動的註冊表數據,同時過時掉ReadWriteCacheMap。
- 此過程不會影響ReadOnlyCacheMap提供人家查詢註冊表。
- 一段時間內(默認30秒),各服務拉取註冊表會直接讀ReadOnlyCacheMap
- 30秒事後,Eureka Server的後臺線程發現ReadWriteCacheMap已經清空了,也會清空ReadOnlyCacheMap中的緩存
- 下次有服務拉取註冊表,又會從內存中獲取最新的數據了,同時填充各個緩存。
多級緩存機制的優勢是什麼?
- 儘量保證了內存註冊表數據不會出現頻繁的讀寫衝突問題。
- 而且進一步保證對Eureka Server的大量請求,都是快速從純內存走,性能極高。
爲方便你們更好的理解,一樣來一張圖,你們跟着圖再來回顧一下這整個過程:
4、總結
- 經過上面的分析能夠看到,Eureka經過設置適當的請求頻率(拉取註冊表30秒間隔,發送心跳30秒間隔),能夠保證一個大規模的系統每秒請求Eureka Server的次數在幾百次。
- 同時經過純內存的註冊表,保證了全部的請求均可以在內存處理,確保了極高的性能
- 另外,多級緩存機制,確保了不會針對內存數據結構發生頻繁的讀寫併發衝突操做,進一步提高性能。
上述就是Spring Cloud架構中,Eureka做爲微服務註冊中心能夠承載大規模系統天天千萬級訪問量的原理。
推薦一個學習圈子:697-57-9-751 裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多