大型系統架構中,會拆分多個子系統。簡單來講,這些子系統有兩個功能:提供接口、調用接口,在微服務架構中,將每個這樣的子系統稱爲一個「微服務」;spring
每個服務會部署多個實例(就是多臺機器,且會動態擴容,IP不固定);
這種狀況下,須要使用eureka進行服務管理。服務ID/名稱 是惟一的標識, 接口調用前,根據ID在註冊中心找到對應的實例信息(ip端口等),而後再直調服務。服務器
概念入門:架構
CAP理論,app
C 數據的一致性 不是強一致負載均衡
A 必定時間內必定有數據返回 error 提示信息分佈式
P 服務容錯性 一個系統中,咱們的服務容許某一塊出錯。不影響系統總體的運行。微服務
Dubbo –cppost
Netflix – APthis
完成微服務,咱們須要作到:spa
Spring cloud
官網:https://spring.io/projects/spring-cloud
項目建立地址:https://start.spring.io/
1.新增eureka服務端(可配置多個實現高可用)
2.新增eureka客戶端進行註冊
*註冊中心(很是重要):
1.什麼是註冊中心
註冊中心能夠說是微服務架構中的」通信錄「,它記錄了服務和服務地址的映射關係。在分佈式架構中,服務會註冊到這裏,當服務須要調用其它服務時,就到這裏找到服務的地址,進行調用。
2.爲何須要註冊中心
在分佈式系統中,咱們不只僅是須要在註冊中心找到服務和服務地址的映射關係這麼簡單,咱們還須要考慮更多更復雜的問題:
服務註冊後,如何被及時發現?
服務宕機後,如何及時下線?
服務如何有效的水平擴展?
服務發現時,如何進行路由?
服務異常時,如何進行降級?
註冊中心如何實現自身的高可用?
這裏問題的解決都依賴於註冊中心。簡單看,註冊中心的功能有點相似於DNS服務器或者負載均衡器,而實際上,註冊中心做爲微服務的基礎組件,可能要更加複雜,也須要更多的靈活性和時效性。
在spring cloud中註冊中心有eureka,consul,zookeeper,nacos等.
咱們這堂課就是以eureka來了解和認知註冊中心的:
假設如今咱們去設計一個eureka,咱們要怎麼去分析:
自動裝配:
spring-cloud-netflix-eureka-server.jar META-INFO/Spring.factors EurekaServerAutoConfiguration
spring-cloud-netflix-eureka-client.jar META-INFO/Spring.factors EurekaClientAutoConfiguration EurekaDiscoveryClientConfiguration
分析:註冊流程,咱們要怎麼去作?
Server配置信息 server 信息: EurekaServerConfigBean#EurekaServerConfig
Client配置信息 instance信息: EurekaClientAutoConfiguration#EurekaApplicationInfo()#create()*在這個方法裏傳入了一個接口,它的實現類: EurekaInstanceConfigBean
Client配置信息 client: EurekaClientAutoConfiguration# eurekaClientConfigBean
New 一個eurekaClientConfigBean 這裏定義了client的配置
EurekaClientAutoConfiguration#eurekaClient()*實例化客戶端client跟蹤
DiscoveryClient 找定時任務,而後向下看,會有一句提示:啓動全部定時任務,跟蹤進去,找到heartbeat—而後會看到renew,renew規則:若是當前實例是404狀態,就去註冊,若是不是,返回true。
註冊中心的請求:
courrentHashmap中
高可用這一塊咱們先去理解一下:對等. P2P 對等實例來實現的高可用 (peer to peer)
老規矩,咱們繼續去尋找:EurekaServerAutoConfiguration下去尋找咱們要的東西.
咱們會看到eurekaServerContext() 這個方法.經過方法名,咱們猜都能猜出來這個就是初始化上下文容器。
那麼咱們來看一下它在初始化幹了什麼?它new 了一個對象 進入這個對象繼續深刻:
咱們能夠在這裏看到,這個類初始化的時候會加載一個對象peerEurekaNodes.start()
位置DefaultEurekaServerContext#peerEurekaNodes 那麼咱們進入它的start裏面去繼續看,
咱們會發現它其實是調用了updatePeerEurekaNodes(resolvePeerUrls) 這個resolvePeerUrls咱們能夠看到,它實際上就是咱們經過實例獲取到的zone,也就是咱們本身定義的集羣地址的集合.
回頭繼續看updatePeerEurekaNodes()方法, 裏面會調用一個共有變量newPeerUrls --夥伴節點地址。它會在這裏定義兩個Set,一個是toAdd(),一個是toshutdown(),這裏面咱們能夠看,它是去維護了新加入的實例,以及須要移除的實例,而後根據一系列的操做判斷,把已經終止的實例移除,再把新加入的實例寫入。
那麼咱們也許會有疑問,若是說咱們在運行期某個節點出現了故障,咱們該怎麼辦?咱們會發現PeerEurekaNodes#start()方法下作了一個定時任務,這個定時任務,會不斷的維護咱們的eureka列表。
同步數據,我以爲咱們就須要來看一下它是怎麼維護實例的。跟進一下
PeerAwareInstanceRegistryImpl這個類咱們以前看到它怎麼註冊的,咱們能夠繼續跟進一下它的註冊流程.
進入到PeerAwareInstanceRegistryImpl以後,咱們能夠看到,它在註冊結束後,還調用了replicateToPeers 複製數據給其餘節點,咱們進入這個方法去看一下
咱們看到了很是熟悉的peerEurekaNodes,它維護了咱們集羣下的服務節點。因此咱們的同步方法就是在遍歷咱們的集羣節點,而後將相應的數據進行同步.(說白了,就是把相應實例再一次註冊給不一樣的服務端)
這樣作有沒有問題?咱們去思考一下,實際上是有的,咱們把數據給到了其餘節點,那麼其餘節點能不能接收到咱們當前的節點是新加的,仍是複製過來的呢?咱們繼續看代碼--它還定義了一個isReplication做爲標誌,若是是複製過來的實例,就讓它爲true,這就表示了它是一個複製過來的數據,防止出現死循環。
1:cancelScheduledTasks();關停線程池
2:判斷咱們的當前客戶端確實註冊在了eureka上面,就去把它的當前狀態改成DOWN。
3:調用unregister();而後去調用了cancel()方法將服務刪除。
進入initEurekaServerContext會看到 registry.openForTraffic(applicationInfoManager, registryCount);這段代碼引用了openForTraffic()方法
進入方法,咱們繼續去追發現它是一個接口,咱們去查看實現類會找到InstanceRegistry下的實現,而後發現它其實是調用了super()方法,因此咱們去找到它的父類,看看這裏是怎麼實現的。
1.updateRenewsPerMinThreshold()這裏會去調用一個公式
當前發送過服務的數量X(60/發送心跳的間隔)X定義的閾值
而後他在這裏對咱們的instance作了一系列的處理,最後去調用了postInit()方法
進入postinit方法,會發現evictionTaskRef.set(new EvictionTask())初始化了一個EvictionTask。
到這個EvictionTask裏面去看,咱們會看到EvictionTask的run方法中調用了一個evict()方法。
這個evict方法就有說法了。這裏就是在處理相關的服務的東西.首先evict方法會去判斷一下isLeaseExpirationEnabled() 也就是是否實現了自我保護機制?若是咱們實現了自我保護機制,
能夠去看一下,若是沒用啓用,它會直接返回一個true,若是啓用了,這裏須要知足如下條件,
服務實例個數大於0,每分鐘的心跳須要大於咱們的閾值,而咱們的閾值就是
當前發送過服務的數量X(60/發送心跳的間隔)X定義的閾值,假設咱們服務已經關停,那麼這裏的第二個條件必定不會知足,因此會返回一個false。因此這裏就會直接return。不去剔除任何服務。因此,在自我保護機制開啓下,eureka不會去主動剔除服務。
回到evict方法裏,繼續去看非自我保護下,咱們會看到evict方法定義了一個集合。將已經沒有發送心跳的實例寫入到expiredLeases這個集合中去。
經過expiredLeases集合,調用internalCancel()會開始去一個個去刪除存在於這個集合中的客戶端。
咱們繼續進入PeerAwareInstanceRegistryImpl下的 isLeaseExpirationEnabled()咱們能夠看到,它去讀取了一個配置項isSelfPreservationModeEnabled 這個就是咱們的自我保護機制的開啓,自我保護說的是什麼意思?保護的是在節點上的服務,不是第一時間被剔除,默認是超過了90s,再去剔除服務。那麼這樣,會出現一個問題,當咱們去訪問時,它有可能拿到的是一個不正常的節點,從而調用失敗,因此咱們才須要降級和熔斷機制。
this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews
* (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds())
* serverConfig.getRenewalPercentThreshold());
1min =60s
1 * (60/30).*0.85 = 1.7