
什麼是Eureka
Eureka 是一個基于 REST(Representational State Transfer)的服務,主要用于 AWS cloud, 提供服務定位(locating services)、負載均衡(load balancing)、故障轉移(failover of middle-tier servers)。是 Netflix 開源的服務發現元件,包括 Server 和 Client 兩部分.在 Spring Cloud 子項目 Spring Cloud Netflix 中。
Eureka Server 和 Eureka Client:
- Eureka Server : 提供服務發現的能力,各服務啟動時,會向 Eureka Server 注冊自己的資訊(IP,端口,服務資訊等),Eureka Server 會存儲這些資訊.
- Eureka Client : 服務提供者,簡化與Eureka Server的互動.
Eureka能做什麼
微服務的注冊與發現,功能類似于 Zookeeper。
原理講解
- Eureka 采用了 C-S 的架構設計,Eureka Server 作為服務注冊功能的伺服器,提供服務發現的能力。
- 微服務啟動後,會周期性(預設30秒)的向 Eureka Server 發送心跳以續約自己的”租期”。
- 如果 Eureka Server 在一定時間内沒有收到某個微服務執行個體(Eureka Client)的心跳,Eureka Server 将會登出該執行個體(預設90秒)。
- 預設請求下,Eureka Server 同時也是 Eureka Client。多個 Eureka Server 執行個體,互相之間通過複制的方式,來實作服務系統資料庫中的資料.(實作叢集,高可用)
- Eureka Client 會緩存系統資料庫中的資訊.這種方式有一定的優勢。首先,微服務無需每次請求都查詢Eureka Server,進而降低了 Eureka Server 的壓力;其次,即使 Eureka Server 所有節點都宕掉,服務消費者依然可以使用緩存中的資訊找到服務提供者并完成調用。這樣,Eureka通過心跳檢測,用戶端緩存等機制,提高了系統的靈活性,可伸縮性和可用性。
畫一張簡單的功能圖來展示其作用:
可以發現同 Zookeeper 功能很相似。
Eureka Server示例
1、建立一個名為 springcloud-config-eureka-7001 的普通 maven 項目
2、導入相關依賴
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<!--熱部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
3、
resources
中添加
application.yml
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: localhost #Eureka服務端的執行個體名稱
client:
register-with-eureka: false #表示是否向注冊中心注冊自己
fetch-registry: false #為false表示自己是注冊中心
service-url: #監控頁面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4、編寫入口類
@EnableEurekaServer //啟動服務發現,接受注冊
@SpringBootApplication
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class, args);
}
}
5、啟動項目,通路 http://localhost:7001/ ,頁面顯示如下:
Eureka Client示例
基于我們之前建立的 Rest 服務提供者 springcloud-provider-dept-8001 進行修改。
1、導入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
2、在入口類上增加
@EnableEurekaClient
注解
@SpringBootApplication
@EnableEurekaClient
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class,args);
}
}
3、
application.yml
檔案中增加 Eureka 配置
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept-8001 # 修改eureka上的預設描述資訊
prefer-ip-address: true # true,可以顯示服務的IP位址
4、啟動項目,通路 http://localhost:7001/ ,頁面結果如下:
當我們點選 Status 标簽下的連結時,頁面會進行跳轉,結果如下所示:
那麼我們考慮如何讓它正常顯示内容。
actuator完善監控資訊
1、首先導入依賴
<!--actuator完善監控資訊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、
application.yml
檔案中增加 info 配置
#info配置
info:
app.name: hresh-springcloud
company.name: blog.csdn.net/Herishwater
3、稍等片刻,再次點選上述連結,内容展示如下:
服務發現Discovery
在團隊多服務建構過程中,如果别人想擷取你所建構的微服務資訊,可以通過注入
org.springframework.cloud.client.discovery.DiscoveryClient
,擷取到微服務清單,然後選擇想要的服務ID,周遊列印具體資訊。
在 DeptController 增加實作方法。
//擷取一些配置的資訊,得到具體的微服務
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping("/dept/discovery")
public Object getService(){
List<String> services = discoveryClient.getServices();
System.out.println("discovery=>services:"+services);
List<ServiceInstance> instances = discoveryClient.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost()+"t"+
instance.getPort()+"t"+
instance.getUri()+"t"+
instance.getServiceId()
);
}
return discoveryClient;
}
前台通路 http://localhost:8001/dept/discovery ,頁面展示結果如下:
{"discoveryClients":[{"services":["springcloud-provider-dept"],"order":0},{"services":[],"order":0}],"services":["springcloud-provider-dept"],"order":0}
叢集
在上文中我們建立了一個名為 springcloud-config-eureka-7001 的普通 maven 項目,參考該項目,再建立兩個僅名字不同的項目,内容基本一緻,對
application.yml
稍作修改:
springcloud-config-eureka-7001
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: eureka7001 #Eureka服務端的執行個體名稱
client:
register-with-eureka: false #表示是否向注冊中心注冊自己
fetch-registry: false #為false表示自己是注冊中心
service-url: #監控頁面
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#叢集(關聯)
defaultZone: http://eureka7002:7002/euraka/,http://eureka7003:7003/euraka/
springcloud-config-eureka-7002
server:
port: 7002
#Eureka配置
eureka:
instance:
hostname: eureka7002 #Eureka服務端的執行個體名稱
client:
register-with-eureka: false #表示是否向注冊中心注冊自己
fetch-registry: false #為false表示自己是注冊中心
service-url: #監控頁面
defaultZone: http://eureka7001:7001/euraka/,http://eureka7003:7003/euraka/
springcloud-config-eureka-7003
server:
port: 7003
#Eureka配置
eureka:
instance:
hostname: eureka7003 #Eureka服務端的執行個體名稱
client:
register-with-eureka: false #表示是否向注冊中心注冊自己
fetch-registry: false #為false表示自己是注冊中心
service-url: #監控頁面
defaultZone: http://eureka7002:7002/euraka/,http://eureka7001:7001/euraka/
因為我們本地端口預設為 localhost,實際上指向的是 127.0.0.1,為了更好地了解叢集的思想,我們将修改 C:WindowsSystem32driversetc 下的 host 檔案,内容如下:
127.0.0.1 eureka7001
127.0.0.1 eureka7002
127.0.0.1 eureka7003
注意:上述内容必須進行修改,否則沿用 localhost,無法得到正确的結果。 依次啟動這3個項目,通路 http://eureka7001:7001/ ,頁面展示結果為:
接着修改 springcloud-provider-dept-8001 的配置檔案如下:
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/
instance:
instance-id: springcloud-provider-dept-8001 # 修改eureka上的預設描述資訊
prefer-ip-address: true # true,可以顯示服務的IP位址
啟動服務提供項目 springcloud-provider-dept-8001,再次通路 http://eureka7001:7001/,頁面展示結果為:
依次再通路 http://eureka7002:7002/ 和 http://eureka7003:7003/ 時,是看不到服務的注冊,雖然這3個注冊中心構成了一個叢集,但是隻有當一個垮了之後,才會去通路下一個叢集節點。 Eureka Client 在發送注冊請求時,會按照 Eureka Server 叢集節點 serviceUrlList 順序逐個去嘗試,如果有一個請求成功了,那麼直接傳回response ,不再去向其他節點請求。
在本例中服務注冊請求在 eureka7001 中注冊成功,即 eureka7001 對應的 Eureka Server服務的狀态是UP,則不會向另外兩個節點(eureka7002,eureka7003)發送請求,相應地頁面上也就沒有顯示。一旦停止 eureka7001 服務注冊中心,則 dept-8001 服務會向 eureka7002 發送注冊請求。
關于此處的結果本人疑惑了很久,在網上一直沒有找到合适的答案,最後在 Eureka高可用之Client重試機制:RetryableEurekaHttpClient 一文中解開了自己的疑惑。
問題記錄
當叢集中服務注冊中心正常使用時,控制台會列印出以下内容:
2020-04-29 22:51:06.468 ERROR 14732 --- [t_eureka7003-16] c.n.e.cluster.ReplicationTaskProcessor : Batch update failure with HTTP status code 404; discarding 1 replication tasks
2020-04-29 22:51:06.468 ERROR 14732 --- [et_eureka7002-3] c.n.e.cluster.ReplicationTaskProcessor : Batch update failure with HTTP status code 404; discarding 1 replication tasks
2020-04-29 22:51:06.469 WARN 14732 --- [t_eureka7003-16] c.n.eureka.util.batcher.TaskExecutors : Discarding 1 tasks of TaskBatchingWorker-target_eureka7003-16 due to permanent error
2020-04-29 22:51:06.469 WARN 14732 --- [et_eureka7002-3] c.n.eureka.util.batcher.TaskExecutors : Discarding 1 tasks of TaskBatchingWorker-target_eureka7002-3 due to permanent error
2020-04-29 22:51:23.489 INFO 14732 --- [a-EvictionTimer] c.n.e.registry.AbstractInstanceRegistry : Running the evict task with compensationTime 0ms
在網上一番查詢之後無果,如果有大神知道此處如何解決,望不吝賜教。
安全驗證
在 springcloud-config-eureka-7001 項目中,我們通路 http://eureka7001:7001/,該注冊中心的面闆是公開通路的。這裡可以簡單加入使用者名密碼,讓通路更安全。
1、添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、配置 application.yml
server:
port: 7001
#Eureka配置
eureka:
instance:
hostname: eureka7001 #Eureka服務端的執行個體名稱
prefer-ip-address: true
client:
register-with-eureka: false #表示是否向注冊中心注冊自己
fetch-registry: false #為false表示自己是注冊中心
service-url: #監控頁面
#單機
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
#叢集(關聯)
# defaultZone: http://eureka7002:7002/euraka/,http://eureka7003:7003/euraka/
spring:
application:
name: eureka-server
security:
user:
name: admin
password: admin
3、啟動該項目,再次通路 http://eureka7001:7001/,效果如下:
擴充
CAP原則
CAP 原則又稱CAP定理,指的是在一個分布式系統中, Consistency(一緻性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。
CAP原則是NOSQL資料庫的基石。
分布式系統的CAP理論:理論首先把分布式系統中的三個特性進行了如下歸納:
- 一緻性(C):在分布式系統中的所有資料備份,在同一時刻是否同樣的值。(等同于所有節點通路同一份最新的資料副本)
- 可用性(A):在叢集中一部分節點故障後,叢集整體是否還能響應用戶端的讀寫請求。(對資料更新具備高可用性)
- 分區容忍性(P):以實際效果而言,分區相當于對通信的時限要求。系統如果不能在時限内達成資料一緻性,就意味着發生了分區的情況,必須就目前操作在C和A之間做出選擇。
著名的CAP理論指出,一個分布式系統不可能同時滿足C(一緻性)、A(可用性)和P(分區容錯性)。由于分區容錯性在是分布式系統中必須要保證的,是以我們隻能在A和C之間進行權衡。在此Zookeeper保證的是CP, 而Eureka則是AP。
Zookeeper保證CP 當向注冊中心查詢服務清單時,我們可以容忍注冊中心傳回的是幾分鐘以前的注冊資訊,但不能接受服務直接down掉不可用。也就是說,服務注冊功能對可用性的要求要高于一緻性。但是zk會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時,剩餘節點會重新進行leader選舉。問題在于,選舉leader的時間太長,30 ~ 120s, 且選舉期間整個zk叢集都是不可用的,這就導緻在選舉期間注冊服務癱瘓。在雲部署的環境下,因網絡問題使得zk叢集失去master節點是較大機率會發生的事,雖然服務能夠最終恢複,但是漫長的選舉時間導緻的注冊長期不可用是不能容忍的。 Eureka保證AP Eureka看明白了這一點,是以在設計時就優先保證可用性。Eureka各個節點都是平等的,幾個節點挂掉不會影響正常節點的工作,剩餘的節點依然可以提供注冊和查詢服務。而Eureka的用戶端在向某個Eureka注冊或時如果發現連接配接失敗,則會自動切換至其它節點,隻要有一台Eureka還在,就能保證注冊服務可用(保證可用性),隻不過查到的資訊可能不是最新的(不保證強一緻性)。除此之外,Eureka還有一種自我保護機制,如果在15分鐘内超過85%的節點都沒有正常的心跳,那麼Eureka就認為用戶端與注冊中心出現了網絡故障,此時會出現以下幾種情況:
- Eureka不再從注冊清單中移除因為長時間沒收到心跳而應該過期的服務
- Eureka仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節點上(即保證目前節點依然可用)
- 當網絡穩定時,目前執行個體新的注冊資訊會被同步到其它節點中
是以, Eureka可以很好的應對因網絡故障導緻部分節點失去聯系的情況,而不會像zookeeper那樣使整個注冊服務癱瘓。
詳情代碼通路: https://github.com/Acorn2/springcloud-eureka ,如有疑惑可聯系我。
參考文獻
Eureka高可用之Client重試機制:RetryableEurekaHttpClient
spring cloud eureka注冊原理-注冊失敗填坑
分布式理論(一) - CAP定理
SpringCloud學習1-服務注冊與發現(Eureka)
學習SpringCloud Eureka帶你從0到1