分布式系統(tǒng)面試系列02-Spring Cloud 的底層架構(gòu)原理颈渊,前面我們講了SpringCloud 的核心架構(gòu)夹界,了解了有要構(gòu)建一套分布式系統(tǒng)我們需要哪些組件赌莺。今天以 SpringCloud 為例南蹂,講解一下它的核心組件的原理算凿。
前面我們講了一個以Spring Cloud 技術(shù)棧實現(xiàn)的分布式系統(tǒng)凳谦,至少得包含 Eureka忆畅、Ribbon、Feign尸执、Zuul 這么幾個組件家凯,你還能記得他們各自是干嘛的么。記不清了沒關(guān)系剔交,回去看一下這篇文章就好肆饶。
Eureka
原理:
1. 是純正的?servlet?應(yīng)用,需構(gòu)建成war包部署?
2. 使用了?Jersey?框架(如注解@POST岖常、@Consumers)實現(xiàn)自身的 RESTful HTTP接口?
3.?peer之間的同步與服務(wù)的注冊全部通過?HTTP?協(xié)議實現(xiàn)?
4.?定時任務(wù)(發(fā)送心跳續(xù)約驯镊、定時清理過期服務(wù)Eviction、節(jié)點同步等)通過 JDK 自帶的?Timer?實現(xiàn)?
5.?內(nèi)存緩存使用Google的guava包實現(xiàn)
首先竭鞍,我們得說說服務(wù)注冊中心 Eureka 了板惑,它應(yīng)該是SpringCloud 技術(shù)棧中最核心的東西。
服務(wù)注冊與發(fā)現(xiàn)怎么實現(xiàn)的
服務(wù)注冊與發(fā)現(xiàn)是 Eureka 中最核心的東西偎快。
比如現(xiàn)在我們有一個服務(wù)消費者 服務(wù)A冯乘,和兩個節(jié)點的服務(wù)提供者,服務(wù)B晒夹。服務(wù)A 和服務(wù)B 在啟動的時候都會向注冊中心進行服務(wù)注冊裆馒。
服務(wù)A 也會定時從服務(wù)注冊中心定時去拉取服務(wù)注冊表信息到本地來,這個過程叫服務(wù)發(fā)現(xiàn)丐怯,默認是30S 一次喷好,當然了可以自己去配置。
如下圖:
實際上當服務(wù)在拉取服務(wù)注冊表的時候读跷,其實客戶端不是直接從 Eureka 中的 服務(wù)注冊表中獲取數(shù)據(jù)的梗搅。
Eureka 做了二級緩存,第一級叫做 ReadOnly 緩存,二級叫做 ReadWrite 緩存无切。
客戶端會直接從ReadOnly 緩存中讀取注冊表信息荡短。
當服務(wù)在進行注冊的時候,先往服務(wù)注冊表中寫入注冊信息哆键,服務(wù)注冊表更新了掘托,立馬會同步一份數(shù)據(jù)到 ReadWrite 緩存中去。
那什么時候?ReadWrite?緩存中的數(shù)據(jù)會到?ReadOnly?緩存中去?
此時有一個定時任務(wù)會定時去檢查 ReadWrite 是否跟? ReadOnly 不一致,不一致就把數(shù)據(jù)同步到 ReadOnly 中去洼哎。
這個定時任務(wù)也默認是 30S烫映。也可以自己配置。
大家可以考慮一下噩峦,這么做的好處是什么,為什么要這么去做二級緩存抽兆?
這么做的好處在于识补,優(yōu)化并發(fā)讀寫的沖突。
如果服務(wù)進行注冊的時候辫红,同時有服務(wù)來讀去注冊表信息凭涂,就會存在頻繁的讀寫加鎖的操作,寫的時候就不能讀贴妻,導(dǎo)致性能下降切油,所以我們需要避免大量的讀寫都去操作一個表。
那么有了這兩層名惩,其實大部分的讀操作都會走 ReadOnly 緩存澎胡。只需要定時把 ReadWrite 緩存中的數(shù)據(jù)寫入到 ReadOnly 就好了。
心跳與故障檢測
服務(wù)注冊中心還有一個很重要的功能就是 心跳與故障檢查娩鹉。心跳跟故障檢測其實就是為了知道注冊上來的這些服務(wù)是不是還活著的攻谁。
Eureka 還會開啟一個定時任務(wù)定時去檢查心跳,默認也是30秒弯予,也可以自己設(shè)置戚宦。
當出現(xiàn)機器故障沒有在約定的時間間隔內(nèi)上報自己的狀態(tài),那么Eureka 就會把這臺機器剔除注冊表锈嫩,同時更新到 ReadWrite 緩存中去受楼。如圖:
但是把數(shù)據(jù)從ReadWrite 緩存同步到 ReadOnly 緩存是有時間間隔的。當服務(wù)消費者A 也只有等待下一次請求更新的時候才會把自己列表里面的服務(wù)給更新掉呼寸。
所以有時候會出現(xiàn)你注冊上去的服務(wù)經(jīng)過及時秒才被服務(wù)消費者發(fā)現(xiàn)艳汽,或者服務(wù)的某個節(jié)點出現(xiàn)故障,沒有及時剔除掉等舔。這里就是同步機制的時間差問題骚灸。
以上就是 Eureka 的核心運行原理了。
Feign & Ribbon
Feign慌植,它其實就是對一個接口打了一個注解甚牲,它會針對這個注解標注的接口生成動態(tài)代理對象义郑,然后針對你的 feign 的動態(tài)代理代理對象去調(diào)用他方法的時候,此時會在底層生成丈钙,http 協(xié)議格式的請求如:/order/create?productId=1
Feign底層的使用的HTTP 通信框架 HttpClient ,先會使用 Ribbon 從本地的 Eureka 注冊表的緩存里面取出要調(diào)用服務(wù)的機器列表出來非驮,然后根據(jù)負載均衡算法,選擇一臺機器出來雏赦,然后針對選擇出來的機器發(fā)送 Http 請求過去劫笙。
Zuul
Zuul 配置請求路徑與服務(wù)的對應(yīng)關(guān)系,你的請求到網(wǎng)關(guān),他就直接查找到匹配的服務(wù),然后就直接把請求轉(zhuǎn)發(fā)給那個服務(wù)的某臺機器, Ribbon 從 Eureka 本地緩存列表里面獲取一臺機器,然后通過負載均衡算法選擇一臺,把請求直接用 http 通信框架發(fā)送到指定的機器上面去。
Hystrix
在微服務(wù)的架構(gòu)中星岗,會存在很多的服務(wù)調(diào)用填大,如果一個服務(wù)出現(xiàn)故障,就很容易導(dǎo)致整個調(diào)用鏈發(fā)生故障俏橘,發(fā)生服務(wù)雪崩的情況允华。
例如,當一個服務(wù)出現(xiàn)故障寥掐,或者超時的問題靴寂,但是服務(wù)調(diào)用方不知道,一直在發(fā)送請求過去召耘,那么等待的請求越來越多百炬,形成任務(wù)積壓,最終導(dǎo)致服務(wù)崩潰污它,癱瘓剖踊。
Hystrix 的出現(xiàn)就是為了解決這種問題。它提供了服務(wù)降級轨蛤、服務(wù)熔斷蜜宪、線程和信號隔離、請求緩存祥山、請求合并以及服務(wù)監(jiān)控等強大功能圃验。
Hystrix使用艙壁模式實現(xiàn)線程池的隔離,它會為每一個依賴服務(wù)創(chuàng)建一個獨立的線程池缝呕,這樣就算某個依賴服務(wù)出現(xiàn)延遲過高的情況澳窑,也只是對該依賴服務(wù)的調(diào)用產(chǎn)生影響,而不會拖慢其他的依賴服務(wù)供常。