一、Eureka簡介
本文中所有程式碼都會上傳到git上,請放心瀏覽
專案git地址:https://github.com/839022478/Spring-Cloud
在傳統應用中,元件之間的呼叫,通過有規範的約束的介面來實現,從而實現不同模組間良好的協作。但是被拆分成微服務後,每個微服務例項的網路地址都可能動態變化,數量也會變化,使得原來硬編碼的地址失去了作用。需要一箇中心化的元件來進行服務的登記和管理,為了解決上面的問題,於是出現了服務治理,就是管理所有的服務資訊和狀態,也就是我們所說的註冊中心
1.1 註冊中心
比如我們去做火車或者汽車,需要去買票乘車,只看我們有沒有票(有沒有服務),有就去買票(獲取註冊列表),然後乘車(呼叫),不用關心到底有多少車在執行
流程圖:
使用註冊中心,我們不需要關心有多少提供方,只管去呼叫就可以了,那麼註冊中心有哪些呢?
註冊中心:Eureka,Nacos,Consul,Zookeeper
本文中講解的是比較火熱的Spring Cloud微服務下的Eureka,Eureka是Netflix開發的服務發現框架,是一個RESTful風格的服務,是一個用於服務發現和註冊的基礎元件,是搭建Spring Cloud微服務的前提之一,它遮蔽了Server和client的互動細節,使得開發者將精力放到業務上。
服務註冊與發現主要包括兩個部分:服務端(Eureka Server)和客戶端(Eureka Client)
-
服務端(Eureka Server): 一個公共服務,為Client提供服務註冊和發現的功能,維護註冊到自身的Client的相關資訊,同時提供介面給Client獲取登錄檔中其他服務的資訊,使得動態變化的Client能夠進行服務間的相互呼叫。
-
客戶端(Eureka Client): Client將自己的服務資訊通過一定的方式登記到Server上,並在正常範圍內維護自己資訊一致性,方便其他服務發現自己,同時可以通過Server獲取到自己依賴的其他服務資訊,完成服務呼叫,還內建了負載均衡器,用來進行基本的負載均衡
Eureka GIt官網:https://github.com/Netflix/Eureka
1.3 服務註冊與發現
服務註冊與發現關係圖:
1.2 client功能和server功能
1.2.1 client功能
- 註冊:每個微服務啟動時,將自己的網路地址等資訊註冊到註冊中心,註冊中心會儲存(記憶體中)這些資訊。
- 獲取服務登錄檔:服務消費者從註冊中心,查詢服務提供者的網路地址,並使用該地址呼叫服務提供者,為了避免每次都查登錄檔資訊,所以client會定時去server拉取登錄檔資訊到快取到client本地。
- 心跳:各個微服務與註冊中心通過某種機制(心跳)通訊,若註冊中心長時間和服務間沒有通訊,就會登出該例項。
- 呼叫:實際的服務呼叫,通過登錄檔,解析服務名和具體地址的對應關係,找到具體服務的地址,進行實際呼叫。
1.2.2 server註冊中心功能
- 服務登錄檔:記錄各個微服務資訊,例如服務名稱,ip,埠等。
登錄檔提供 查詢API(查詢可用的微服務例項)和管理API(用於服務的註冊和登出)。 - 服務註冊與發現:註冊:將微服務資訊註冊到註冊中心。發現:查詢可用微服務列表及其網路地址。
- 服務檢查:定時檢測已註冊的服務,如發現某例項長時間無法訪問,就從登錄檔中移除。
二、Eureka單節點搭建
2.1 pom.xml
在有的教程中,會引入spring-boot-starter-web
,這個依賴其實不用,因為spring-cloud-starter-netflix-eureka-server
的依賴已經包含了它,在pom依賴進去,就可以了
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2.2 application.yml
server:
port: 8500
eureka:
client:
#是否將自己註冊到Eureka Server,預設為true,由於當前就是server,故而設定成false,表明該服務不會向eureka註冊自己的資訊
register-with-eureka: false
#是否從eureka server獲取註冊資訊,由於單節點,不需要同步其他節點資料,用false
fetch-registry: false
#設定服務註冊中心的URL,用於client和server端交流
service-url:
defaultZone: http://localhost:8080/eureka/
2.3 服務端啟動類
啟動類上新增此註解標識該服務為配置中心
@EnableEurekaServer
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
2.4 啟動
我們啟動EurekaDemoApplication
,然後在瀏覽器中輸入地址 http://localhost:8500/
,就可以啟動我們的 Eureka 了,我們來看下效果,出現了這個畫面,就說明我們已經成功啟動~,只是此時我們的服務中是還沒有客戶端進行註冊
三、服務註冊
注意:在客戶端pom裡面我們需要加上spring-boot-starter-web
,否則服務是無法正常啟動的
3.1 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.2 application.yml
#註冊中心
eureka:
client:
#設定服務註冊中心的URL
service-url:
defaultZone: http://localhost:8500/eureka/
#服務名
instance:
appname: mxn
3.3 客戶端啟動類
在客戶端啟動類中我們需要加上 @EnableDiscoveryClient
註解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
3.4 檢視效果
工程啟動後,重新整理http://localhost:8500/
頁面,我們可以發現服務註冊成功了
並且我們可以在idea日誌列印中看到DiscoveryClient_MXN/DESKTOP-5BQ3UK8 - registration status: 204
,說明就是註冊成功了
Eureka Server與Eureka Client之間的聯絡主要通過心跳的方式實現。心跳(Heartbeat)即Eureka Client定時向Eureka Server彙報本服務例項當前的狀態,維護本服務例項在登錄檔中租約的有效性。
Eureka Client將定時從Eureka Server中拉取登錄檔中的資訊,並將這些資訊快取到本地,用於服務發現
四、Eureka 端點
官網地址:https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
Eureka伺服器還提供了一個端點(eureka/apps/{applicaitonName})
可以檢視所註冊的服務詳細資訊 。applicaitonName就是微服務的名稱,比如這裡我們訪問 http://localhost:8500/eureka/apps/mxn
五、Eureka 原理
5.1 本質
儲存了每個客戶端的註冊資訊。EurekaClient從EurekaServer同步獲取服務註冊列表。通過一定的規則選擇一個服務進行呼叫
5.2 Eureka架構圖
- 服務提供者: 是一個eureka client,向Eureka Server註冊和更新自己的資訊,同時能從Eureka Server登錄檔中獲取到其他服務的資訊。
- 服務註冊中心: 提供服務註冊和發現的功能。每個Eureka Cient向Eureka Server註冊自己的資訊,也可以通過Eureka Server獲取到其他服務的資訊達到發現和呼叫其他服務的目的。
- 服務消費者: 是一個eureka client,通過Eureka Server獲取註冊到其上其他服務的資訊,從而根據資訊找到所需的服務發起遠端呼叫。
- 同步複製: Eureka Server之間登錄檔資訊的同步複製,使Eureka Server叢集中不同登錄檔中服務例項資訊保持一致。
- 遠端呼叫: 服務客戶端之間的遠端呼叫。
- 註冊: Client端向Server端註冊自身的後設資料以供服務發現。
- 續約: 通過傳送心跳到Server以維持和更新登錄檔中服務例項後設資料的有效性。當在一定時長內,Server沒有收到Client的心跳資訊,將預設服務下線,會把服務例項的資訊從登錄檔中刪除。
- 下線: Client在關閉時主動向Server登出服務例項後設資料,這時Client的服務例項資料將從Server的登錄檔中刪除。
- 獲取登錄檔: Client向Server請求登錄檔資訊,用於服務發現,從而發起服務間遠端呼叫。
5.3 Eureka自我保護
有時候我們會看到這樣的提示資訊:EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
,這是因為預設情況下,Eureka Server在一定時間內,沒有接收到某個微服務心跳,會將某個微服務登出(90S)。但是當網路故障時,微服務與Server之間無法正常通訊,上述行為就非常危險,因為微服務正常,不應該登出,它的指導思想就是 寧可保留健康的和不健康的,也不盲目登出任何健康的服務
我們也可以通過命令去關閉自我保護的功能:
eureka:
server:
enable-self-preservation: false
那麼自我保護是如何觸發的呢?
自我保護機制的觸發條件是,當每分鐘心跳次數( renewsLastMin
) 小於 numberOfRenewsPerMinThreshold
時,並且開啟自動保護模式開關( eureka.server.enable-self-preservation = true
) 時,觸發自我保護機制,不再自動過期租約
上面我們所有的小於 numberOfRenewsPerMinThreshold
,到底是怎麼計算的呢,我們在eureka原始碼中可以得知
numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 續租百分比(預設為0.85)
expectedNumberOfRenewsPerMin = 當前註冊的應用例項數 x 2
當前註冊的應用例項數 x 2 是因為,在預設情況下,註冊的應用例項每半分鐘續租一次,那麼一分鐘心跳兩次,因此 x 2
例如:我們有10個服務,期望每分鐘續約數:10 * 2=20,期望閾值:20*0.85=17,當少於17時,就會觸發自我保護機制
5.4 健康檢查
由於server和client通過心跳保持 服務狀態,而只有狀態為UP的服務才能被訪問。看eureka介面中的status。
比如心跳一直正常,服務一直UP,但是此服務DB(資料庫)連不上了,無法正常提供服務。
此時,我們需要將 微服務的健康狀態也同步到server。只需要啟動eureka的健康檢查就行。這樣微服務就會將自己的健康狀態同步到eureka。配置如下即可。
在client端配置:將自己的健康狀態傳播到server。
eureka:
client:
healthcheck:
enabled: true
5.5 Eureka監聽事件
import com.netflix.appinfo.InstanceInfo;
import org.springframework.cloud.netflix.eureka.server.event.*;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class CustomEvent {
@EventListener
public void listen(EurekaInstanceCanceledEvent event ) {
System.out.println(LocalDateTime.now()+"服務下線事件:"+event.getAppName()+"---"+event.getServerId());
//發釘釘
}
@EventListener
public void listen(EurekaInstanceRegisteredEvent event) {
InstanceInfo instanceInfo = event.getInstanceInfo();
System.out.println(LocalDateTime.now()+"服務上線事件:"+instanceInfo.getAppName()+"---"+instanceInfo.getInstanceId());
}
@EventListener
public void listen(EurekaInstanceRenewedEvent event) {
System.out.println(LocalDateTime.now()+"服務續約/心跳上報事件:"+event.getAppName()+"---"+event.getServerId());
}
@EventListener
public void listen(EurekaRegistryAvailableEvent event) {
System.out.println(LocalDateTime.now()+"註冊中心可用事件");
}
@EventListener
public void listen(EurekaServerStartedEvent event) {
System.out.println(LocalDateTime.now()+"註冊中心啟動事件");
}
}
5.6 Renew: 服務續約
Eureka Client 會每隔 30 秒傳送一次心跳來續約。 通過續約來告知 Eureka Server 該 Eureka Client 執行正常,沒有出現問題。 預設情況下,如果 Eureka Server 在 90 秒內沒有收到 Eureka Client 的續約,Server 端會將例項從其登錄檔中刪除,此時間可配置,一般情況不建議更改。
5.6 服務剔除
如果Eureka Client在註冊後,既沒有續約,也沒有下線(服務崩潰或者網路異常等原因),那麼服務的狀態就處於不可知的狀態,不能保證能夠從該服務例項中獲取到回饋,所以需要服務剔除此方法定時清理這些不穩定的服務,該方法會批量將登錄檔中所有過期租約剔除,剔除是定時任務,預設60秒執行一次。延時60秒,間隔60秒
剔除的限制:
1.自我保護期間不清除。
2.分批次清除。
六、Eureka缺陷
由於叢集間的同步複製是通過HTTP的方式進行,基於網路的不可靠性,叢集中的Eureka Server間的登錄檔資訊難免存在不同步的時間節點,不滿足CAP中的C(資料一致性)
七、總結
中間我們講解了eureka的節點搭建,以及原理,對於現在很火熱的微服務,我們對Eureka是非常有必要進行了解的,如果覺得文章對你有幫助,來個點贊支援吧,如果對文章有疑問或建議,歡迎討論留言,謝謝大家~