什麼是服務註冊?java
首先咱們來了解下,服務註冊、服務發現和服務註冊中心的之間的關係。nginx
舉個形象的例子,三者之間的關係就好像是供貨商,顧客和商店。面試
首先各地的供貨商會將各類商品提供給商店,而後顧客須要商品的時候會去商店購買。算法
註冊中心就比如是這個商店,供貨商的動做就是服務註冊,商品就是註冊的服務。spring
當部署的服務啓動後,會註冊到註冊中心,消費者須要什麼樣的服務,就本身去註冊中心拉取。瀏覽器
那麼到底什麼是服務註冊,爲何要將服務註冊到註冊中心呢?緩存
服務註冊指的是服務在啓動時將服務的信息註冊到註冊中心中,由註冊中心統一對全部的註冊的服務進行管理。安全
如今咱們想一想,假如你是消費者,你須要買一個商品,你能夠去商店,也能夠直接去供貨商。服務器
可是若是今天你要買不少商品,並且咱們並不知道每一個商品對應的供應商的地址,那麼你就得挨家挨戶的去跑供貨商購買商品。微信
是否是就很麻煩了,假若此時有一家商店,彙總了多家供貨商的商品,那你是否是隻須要去這一家商店就能購買到你須要的全部的商品了呢?
這樣是否是就方便了。
在咱們現實開發中,好比咱們須要獲取用戶信息的時候,而此時,咱們的用戶服務只部署了一個節點,那咱們就可使用IP+端口的形式訪問服務。
當此時咱們的服務部署了10個節點後,咱們能夠經過域名訪問,經過nginx轉發到某一個節點上,訪問該節點的服務。
使用過nginx的小夥伴們都知道,每當咱們須要新增一個節點的時候,咱們就須要去修改nginx的配置文件,一旦服務部署的節點過多,頻繁修改配置文件就變成了一件極其麻煩的事情。
這個時候,咱們的註冊中心,就應該粉墨登場了。
註冊中心一登場,咱們就儘管部署服務節點,部署完成後,服務啓動,服務的信息就會被註冊到註冊中心,咱們就不須要擔憂是否是又要去修改配置文件了。
什麼是服務發現?
服務發現有兩種模式:一種是客戶端發現模式,一種是服務端發現模式。Eureka採用的是客戶端發現模式。
客戶端發現模式就比如我是一個土豪顧客,我去了商店,我把全部的商品都買回家,須要的時候在這些商品裏面尋找。
由於我是土豪,因此我固然不能忍受商店裏有新品上架,而我卻沒有,因此我每隔一段時間就會商店增量獲取最新的商品。
這就是Eureka中的Fetch Registry,抓取註冊信息。
Eureka Client 從 Eureka Server 獲取註冊表信息並在本地緩存。
這裏咱們注意一下,Eureka Client並非直接去服務註冊表中獲取數據,而是從ReadOnly緩存中獲取數據。
而且會經過在上一個獲取週期和當前獲取週期之間獲取增量更新,這些信息會按期更新(每30秒更新一次)。
獲取的時候可能返回相同的實例。Eureka Client會自動處理重複信息。
由於個人土豪行爲,我已經被商店老闆記錄在它的VVIP名單上了。惋惜天有不測風雲,我破產了,我不再能這麼土豪了,沒辦法,我告訴了老闆我破產了,老闆聽了個人話,想都沒想,直接從他的VVIP名單上將個人名字給剔除了,社會就是這麼現實。
這就是Eureka中的Cancel,取消。
每個微服務節點關閉時,Eureka Client會向Eureka Server發送一個取消請求。
Eureka Server收到你的取消請求後,就會將你從服務註冊表中給剔除。
商店老闆是個傲嬌的人,她制定了一個規則,若是你是她VVIP名單上的人,你必須每隔一段時間就要去商店採購商品。
一旦你在一段時間內沒有來採購,她就以爲你已經沒有購買能力,不適合在她的VVIP名單上存在了。
她就會狠心的將你從她的VVIP名單上將個人名字給剔除了。
這就是Eureka中的Renew(更新 / 續借)
Eureka Client 內部具有一個內置的負載均衡器,它使用輪訓(round-robin)負載算法。
在服務啓動後,每隔必定週期(默認30秒)向Eureka Server發送心跳。
若是Eureka Server在多個心跳週期內(默認90秒)沒有收到Eureka Client發送過來的心跳,Eureka Server將會在服務註冊表中將該節點剔除。
固然了,服務發現去註冊中心拉取的是服務的信息,而後須要從服務信息中獲取到服務部署的節點信息,而後經過域名地址訪問到該節點的服務。
就好像一家商店,由於空間過小,只是存放了一些商品的微縮模型,模型上寫着該商品所屬的供貨商地址,咱們去商店拿到該模型後,看到供貨商的地址,而後咱們就能夠直接去供貨商那兒直接購買商品了。
什麼是註冊中心,註冊中心的做用?
註冊中心就是一個管理器,各個服務提供者將服務註冊到註冊中心,有註冊中心進行統一的存儲和管理。
註冊中心同時還有着判斷服務是否可用,對於不可用的服務進行剔除的功能。
至於如何判斷服務的可用性和如何剔除不可用的服務,後續會有詳細的講解。
什麼是 Eureka,有什麼做用?
Eureka採用CS架構,它分爲兩大組件。
一個是Eureka Server,註冊中心服務端。
當各個微服務節點啓動後,Eureka Server 會存儲服務提供者註冊上來的服務信息,而且提供二層緩存機制來維護整個註冊中心。
另外一個是Eureka Client,註冊中心客戶端。
Eureka Client是一個java客戶端,它用來簡化和Eureka Server交互。
Eureka Client 會拉取、更新和緩存 Eureka Server 中的信息。
所以當全部的 Eureka Server 節點都宕掉,服務消費者依然可使用緩存中的信息找到服務提供者,可是當服務有更改的時候會出現信息不一致。
Eureka 架構詳解
以下圖所示,這是官網提供給咱們的Eureka的架構圖Eureka 的架構,主要分爲 Eureka Server 和 Eureka Client 兩部分,Eureka Client 又分爲 Applicaton Service 和 Application Client,Applicaton Service 就是服務提供者,Application Client 就是服務消費者。
咱們首先會在應用程序中依賴 Eureka Client,項目啓動後 Eureka Client 會向 Eureka Server 發送請求,進行註冊,並將本身的一些信息發送給 Eureka Server。
註冊成功後,每隔必定的時間,Eureka Client 會向 Eureka Server 發送心跳來續約服務,也就是彙報健康狀態。若是客戶端長時間沒有續約,那麼 Eureka Server 大約將在 90 秒內從服務器註冊表中刪除客戶端的信息。
Eureka Client 還會按期從 Eureka Server 拉取註冊表信息,而後根據負載均衡算法獲得一個目標,併發起遠程調用,關於負載均衡在後面的課時會詳細介紹,也就是 Ribbon 組件。
應用中止時也會通知 Eureka Server 移除相關信息,信息成功移除後,對應的客戶端會更新服務的信息,這樣就不會調用已經下線的服務了,固然這個會有延遲,有可能會調用到已經失效的服務,因此在客戶端會開啓失敗重試功能來避免這個問題。
Eureka Server 會有多個節點組成一個集羣,保證高可用。Eureka Server 沒有集成其餘第三方存儲,而是存儲在內存中。
因此 Eureka Server 之間會將註冊信息複製到集羣中的 Eureka Server 的全部節點。
這樣數據纔是共享狀態,任何的 Eureka Client 均可以在任何一個 Eureka Server 節點查找註冊表信息。
Eureka 的工做流程
Eureka 的自我保護機制
什麼是自我保護機制
官方定義:自我保護模式正是一種針對網絡異常波動時的安全保護措施,使用自我保護模式能使Eureka集羣更加健壯穩定的運行。
爲何要開啓自我保護機制?
若是Eureka Server在必定時間內(默認90s)(可優化)沒有收到某一個服務節點的心跳,Eureka Server將會移除該服務實例。
可是在某些時候,遇到網絡分區故障,服務節點其實是正常存貨狀態,可是卻沒法和Eureka Server正常通訊,此時若是沒有引入自我保護機制,Eureka Server就會將該服務節點剔除。
自我保護模式的工做機制
若是15分鐘內超過85%的客戶端節點都沒有正常的心跳,那麼Eureka Server就會認爲客戶端與註冊中心發生了網絡故障,Eureka Server進入自我保護機制。
自我保護機制的缺點
若是在自我保護機制中,恰好某些服務節點非正常下線,可是Eureka Server並不會剔除該服務節點,服務消費者就會獲取到一個無效的服務實例。
解決方案
① :關閉自我保護機制(不推薦)
② :切換請求或斷路器,使用負載均衡的方式,設置當一個請求超過多少秒還未獲得響應,速度切換請求到下一個註冊服務,例如使用Ribbon+Hystrix配置負載均衡和斷路器。
Eureka Server 進入自我保護機制後
一、Eureka Server不在從註冊表中剔除由於長時間沒有和註冊中心續約的服務節點
二、Eureka Server仍然可以接受新服務的註冊和查詢請求,可是不會同步到其餘Eureka Server節點上
三、網絡正常後,當前Eureka Server節點會將新的服務節點信息同步到其餘Eureka Server節點上
如何開啓自我保護
經過eureka.server.enable-self-preservation=true/false來開啓或關閉自我保護機制。
其餘關鍵配置:
清理失效服務節點的時間間隔:eureka.server.evication-interval-timer-in-ms默認60s
續約間隔時間:eureka.instance.lease-renewal-interval-in-seconds默認30s
續約到期時間:eureka.instance.lease-expiration-duration-in-seconds默認90s
經過源碼窺探Eureka是如何開啓自我保護機制的
第一步,咱們引入Eureka Server 依賴。
第二步,咱們找到eureka-core jar包下的路徑爲com.netflix.eureka下的registry包
第三步,進入AbstractInstanceRegistry 類,找到evict方法,這個是按期剔除任務的線程最終執行的方法
public void evict(long additionalLeaseMs) { logger.debug("Running the evict task"); //判斷自動保護是否開啓,若是開啓了自動保護, //那麼剔除服務的操做就不往下執行,就不會剔除服務了 if (!this.isLeaseExpirationEnabled()) { logger.debug("DS: lease expiration is currently disabled."); } else { ... } }
第四步,咱們找到isLeaseExpirationEnabled()方法的實現
public boolean isLeaseExpirationEnabled() { //首先判斷是否在配置文件中關閉了自我保護,若是關閉了自我保護直接返回true, // evict方法也就能夠繼續往下執行,進行無用服務剔除操做 if (!this.isSelfPreservationModeEnabled()) { return true; } else { //若是開啓了自我保護機制,則進一步判斷是否開啓自我保護機制 //numberOfRenewsPerMinThreshold(每分鐘最小的續約次數)大於0 //並且getNumOfRenewsInLastMin(最後一分鐘的續約次數)必需要大於每分鐘最小的續約次數 //當知足上面兩個條件時則說明續約正常,返回true,evict方法也就能夠繼續往下執行,進行無用服務剔除操做 //因此說,只有不知足上述兩個條件的狀況下,纔會返回false,evict方法也就不在往下執行,即便有沒有正常續約的服務,也不會剔除 return this.numberOfRenewsPerMinThreshold > 0 && this.getNumOfRenewsInLastMin() > (long)this.numberOfRenewsPerMinThreshold; } }
第五步,咱們注意到numberOfRenewsPerMinThreshold這個變量很關鍵,它的含義是每分鐘最小的續約次數
在服務註冊register和服務下線cancel兩個方法中會更新這個變量,更新該變量方法以下:
/** * 指望每分鐘最小的續約次數 = 指望正常續約的服務實例數 * (60/(咱們配置的發送一次心跳時間間隔(默認30秒)))*0.85 * serverConfig.getRenewalPercentThreshold() 默認是 0.85 (能夠經過eureka.server.renewal-percent-threshold)進行配置 **/ protected void updateRenewsPerMinThreshold() { this.numberOfRenewsPerMinThreshold = (int)((double)this.expectedNumberOfClientsSendingRenews * (60.0D / (double)this.serverConfig.getExpectedClientRenewalIntervalSeconds()) * this.serverConfig.getRenewalPercentThreshold()); }
以上就是Eureka開啓自我保護的整個邏輯流程。
解除自我保護機制
1.當服務的網絡分區故障解除以後,客戶端可以和服務進行交互時,在續約的時候,更新每分鐘的續約數,當每分鐘的續約數大於85%時,則自動解除。
2.重啓服務
Eureka 的健康檢查
其實不少框架的健康狀態監控都是經過actuator來管理健康狀態的,而且擴展了health端點。
因此說咱們只要在項目中集成Actuator,咱們就能管理監控項目的健康狀態。
Eureka也是同樣,咱們能夠將某些不健康的服務節點的狀態告知Eureka Server,而後Eureka Server 會主動讓其下線。
這個就是Eureka的健康檢查。
如何實現Eureka-Actuator健康檢查?
首先咱們要在pom中依賴spring-boot-starter-actuator。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.3.3.RELEASE</version> </dependency>
第二步,配置文件中添加 eureka.client.healthcheck.enabled=true 配置
原理分析:
首先在EurekaDiscoveryClientConfiguration 中根據 eureka.client.healthcheck.enabled 的值來決定是否要裝配 EurekaHealthCheckHandler,而後在 EurekaClientAutoConfiguration 中會註冊 HealthCheck,但咱們註冊完成後會有調用任務來進行狀態的更新,在com.netflix.discovery.InstanceInfoReplicator.run() 中會進行狀態更新。
Eureka 的多級緩存機制
什麼是多級緩存機制
Eureka Server 爲了不同事讀取內存數據形成的併發衝突問題,採用了多級緩存機制提高服務請求的響應速度。
Eureka Server的緩存是經過一個只讀,一個讀寫緩存來實現的。
一級緩存:concurrentHashMap<key,value>readOnlyCacheMap本質是HashMap,無過時時間,保存數據信息對外輸出。
readOnlyCacheMap依賴於定時器的更新,經過與readWriteCacheMap的值作對比,以readWriteCacheMap爲準。
responseCacheUpdateIntervalMs:readOnlyCacheMap緩存更新間隔,默認30s
二級緩存:LoaDing<key,value>readWriteCacheMap本質是Guava緩存,包含失效機制,保護數據信息對外輸出。
responseCacheAutoExpirationInSeconds:readWriteCacheMap 緩存過時時間,默認180s。
當服務節點發生註冊,下線,過時,狀態變動等變化時
1.在內存中更新註冊表信息
2.同時過時掉readWriteCacheMap緩存,緩存清除只是會去清除readWriteCacheMap這個緩存, readOnlyCacheMap 只讀 緩存並無更新,也就說當客戶端的信息發生變化以後, 只讀緩存不是第一時間感知到的。只讀緩存的更新只能依賴那個30秒的定時任務來更新。
3.一段時間後(默認30s),後臺線程發現readWriteCacheMap緩存爲空,因而也將readOnlyCacheMap中的緩存清空
4.當有服務消費者拉取註冊表信息時,會調用ClassLoader的load方法,將內存中的註冊表信息加載到各級緩存中,並返回註冊表信息。
在Eureka Server 中會有兩個線程,一個是定時同步兩個緩存的數據,默認30s,一個是定時檢測心跳故障,默認90s。
服務拉取
1.服務消費者,默認每30s,拉取註冊表信息
2.從readOnlyCacheMap中獲取信息,若是獲取爲空
3.從readWriteCacheMap中獲取,若是仍是爲空
4.調用ClassLoader的load方法,將內存中的註冊表信息加載到各級緩存中,並返回註冊表信息。
Eureka的區域配置
當用戶地理分佈範圍很廣的時候,好比公司在上海、杭州、青島等都有分公司的時候,通常都會有多個機房。
那麼對於用戶而言,固然是但願調用本地分公司的機房中的微服務應用。
好比:上海用戶A,調用OAuth2服務,用戶A固然但願調用上海機房裏面的微服務應用。若是上海用戶A調用杭州機房的OAuth2服務,就增長的延時時間。
因此咱們但願一個機房內的服務優先調用同一個機房內的服務,當同一個機房的服務不可用的時候,再去調用其它機房的服務,以達到減小延時的做用。
爲此,eureka提供了region和zone兩個概念來進行分區,Eureka基於Amazon設計的,因此對於地域的區分也與Amazon一致,Amazon分爲多個region,每一個region包含多個zone,因此Eureka設計時也是能夠設置region與zone,請求時能夠優先選擇與請求服務在同一個zone的服務。
基本配置
eureka: client: region: shanghai availability-zones: shanghai: zone1, zone2 service-url: zone1: http://localhost:8081/eureka, http://localhost:8082/eureka zone2: http://localhost:8083/eureka, http://localhost:8084/eureka instance: metadata-map: zone: zone1
此時不管咱們調用多少次,調用的都是shanghai下面的zone1下面的服務。
通常來講咱們都會結合Ribbon來實現微服務的負載均衡,而Ribbon內部會有一些專門的負載策略算法,雖然剛剛咱們說過會優先請求的是指定region下指定 Zone 區域的服務實例。
但有些時候好比當在Region=shanghai下沒有可用的zone,系統會默認加載 DEFAULT_ZONE,或者說活着同區域的服務負載太高...等等,也會自動切換成其餘區域的服務。
Eureka的重試機制
因爲 Spring Cloud Eureka 實現的服務治理機制強調了 CAP 原理中的 AP,便可用性與可靠性,犧牲了必定的一致性(在極端狀況下它寧願接受故障實例也不要丟掉"健康"實例,如同如咱們上面所說的自我保護機制)。
但不管是因爲觸發了保護機制仍是服務剔除的延遲,引發服務調用到這些不正常的服務,調用就會失敗,從而致使其它服務不能正常工做!
這顯然不是咱們願意看到的,咱們仍是但願可以加強對這類問題的容錯。因此,咱們在實現服務調用的時候一般會加入一些重試機制。
從 Camden SR2 版本開始,Spring Cloud 就整合了 Spring Retry 來加強 RestTemplate 的重試能力,對於開發者來講只需經過簡單的配置,原來那些經過 RestTemplate 實現的服務訪問就會自動根據配置來實現重試策略。
開啓Eureka的重試機制很簡單,首先咱們在pom中引入Spring Retry的依賴:
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
而後在配置文件中配置spring.cloud.loadbalancer.retry.enabled參數來控制重試機制的開關,由於 Spring Retry默認是開啓的,因此說咱們只要引入依賴便可,若是說咱們想要關閉的話只須要將 spring.cloud.loadbalancer.retry.enabled設置爲false便可。
其實在SpringCloud的架構組件中不管是Fegin,Ribbon,仍是Zuul都提供了重試機制,這些暫且不論,後續再講到具體的組件時再進行具體的分析。
實戰 Eureka 註冊中心的部署
Eureka Server 項目搭建
一、建立一個 springcloud-eureka-server 的項目,而後在 pom 中增長 spring-cloud-starter-netflix-eureka-server 的依賴。
2.在pom.xml文件中新增spring-cloud-starter-netflix-eureka-server依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
3.在啓動類SpringcloudEurekaServerApplication上使用 @EnableEurekaServer 開啓 EurekaServer 的自動裝配功能。
@SpringBootApplication @EnableEurekaServer public class SpringcloudEurekaServerApplication { public static void main(String[] args){ SpringApplication.run(SpringcloudEurekaServerApplication.class, args); } }
4.添加配置文件application.properties
# server (eureka 默認端口爲:8761) server.port=8761 # spring spring.application.name=spring-cloud-eureka-server # eureka # 是否註冊到eureka eureka.client.register-with-eureka=false # 是否從eureka獲取註冊信息 eureka.client.fetch-registry=false # eureka服務器的地址(注意:地址最後面的 /eureka/ 這個是固定值) eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
5.啓動項目,而後訪問http://localhost:8761/,能夠看到 Eureka 的管理頁面,表示 Eureka 啓動成功了。
Eureka Client 項目搭建
一、建立一個 springcloud-eureka-client 的項目,而後在 pom 中增長 spring-cloud-starter-netflix-eureka-client 的依賴。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
2.在啓動類SpringcloudEurekaClientApplication上使用 @EnableEurekaClient 開啓 EurekaServer 的自動裝配功能。
@SpringBootApplication @EnableEurekaClient public class SpringcloudEurekaClientApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudEurekaClientApplication.class, args); } }
配置 eureka.client.serviceUrl.defaultZone 的地址,也就是剛剛啓動的 Eureka Server 的地址,http://localhost:8761/eureka/。
server.port=8762 spring.application.name=spring-cloud-eureka-client eureka.client.serviceUrl.defaultZoon=http://localhost:8761/eureka/
啓動客戶端,而後刷新剛剛打開的Eureka主頁,咱們發現服務已經註冊成功。
Eureka 註冊中心添加密碼認證
上面咱們看到咱們搭建好Eureka Server 後訪問http://localhost:8761/,沒有登錄就能夠直接看到 Eureka 的管理頁面。
若是在實際使用中,註冊中心地址有公網 IP 的話,必然能直接訪問到,這樣是不安全的。因此咱們須要對 Eureka 進行改造,經過集成 Spring-Security 來進行安全認證,加上權限認證來保證安全性。
首先,在 pom.xml 中引入 Spring-Security 的依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
而後在 application.properties 中加上認證的配置信息
spring.security.user.name=javaer123456 #用戶名 spring.security.user.password=123456 #密碼
最後增長Spring Security 配置類
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 關閉 Spring Security 的 CSRF 驗證(若是不關閉,那麼客戶端就鏈接不上) http.csrf().disable(); // 支持httpBasic http.authorizeRequests().anyRequest().authenticated().and().httpBasic(); } }
這樣咱們啓動Eureka Server成功後在訪問 http://localhost:8761/,此時瀏覽器會提示你輸入用戶名和密碼,輸入正確後才能繼續訪問 Eureka 提供的管理頁面。
注意:在 Eureka Server開啓認證後,Eureka Client註冊的配置也要加上認證的用戶名和密碼信息
eureka.client.serviceUrl.defaultZone=http://javaer23456:123456@localhost:8761/eureka/
總結
本章咱們主要學習了Eureka相關知識,雖然Eureka已經停更了,可是不少公司還在使用它,而它也足夠穩定,它所提供的功能也足夠大多數公司使用。因此說如今面試中,Eureka相關題目仍是常常出現的。反正學到了就是本身的。
祝願你們早日成爲大牛,有一頭烏黑秀髮的大牛。
原創不易,若是你們喜歡,賞個三連吧。和你們一塊兒成爲這世界上最優秀的人。
如下資料關注公衆號,回覆【8888】,便可免費獲取。