Eureka基礎知識

什麼是服務治理

Spring Cloud封裝了Netflix 公司開發的Eureka模組來實現服務治理

在傳統的RPC遠端呼叫框架中,管理每個服務與服務之間依賴關係比較複雜,管理比較複雜,所以需要使用服務治理,管理服務於服務之間依賴關係,可以實現服務呼叫、負載均衡、容錯等,實現服務發現與註冊。

什麼是服務註冊與發現

Eureka採用了CS的設計架構,Eureka Sever作為服務註冊功能的伺服器,它是服務註冊中心。而系統中的其他微服務,使用Eureka的客戶端連線到 Eureka Server並維持心跳連線。這樣系統的維護人員就可以通過Eureka Server來監控系統中各個微服務是否正常執行。

在服務註冊與發現中,有一個註冊中心。當伺服器啟動的時候,會把當前自己伺服器的資訊比如服務地址通訊地址等以別名方式註冊到註冊中心上。另一方(消費者服務提供者),以該別名的方式去註冊中心上獲取到實際的服務通訊地址,然後再實現本地RPC呼叫,RPC遠端呼叫框架核心設計思想:在於註冊中心,因為使用註冊中心管理每個服務與服務之間的一個依賴關係(服務治理概念)。在任何RPC遠端框架中,都會有一個註冊中心存放服務地址相關資訊(介面地址)

Eureka包含兩個元件:

Eureka Server

  • Eureka Server提供服務註冊服務

    • 各個微服務節點通過配置啟動後,會在EurekaServer中進行註冊,這樣EurekaServer中的服務登錄檔中將會儲存所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀看到。

Eureka Client

  • EurekaClient通過註冊中心進行訪問

    • 它是一個Java客戶端,用於簡化Eureka Server的互動,客戶端同時也具備一個內建的、使用輪詢(round-robin)負載演算法的負載均衡器。在應用啟動後,將會向Eureka Server傳送心跳(預設週期為30秒)。如果Eureka Server在多個心跳週期內沒有接收到某個節點的心跳,EurekaServer將會從服務登錄檔中把這個服務節點移除(預設90秒)

單機版Eureka搭建

搭建EurekaServer

建立Maven專案

1.x 與 2.x 對比

<!-- eureka新舊版本 -->
<!-- 以前的老版本(2018)-->
<dependency>
<groupid>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 現在新版本(2020.2)--><!-- 我們使用最新的 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

修改POM.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.dance</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>cloud-eureka-server7001</artifactId> <dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- dependent on common modules -->
<dependency>
<groupId>com.dance</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies> </project>

新增yml配置

server:
port: 7001 eureka:
instance:
hostname: locathost #eureka服務端的例項名稱
client:
#false表示不向註冊中心註冊自己。
register-with-eureka: false
#false表示自己端就是註冊中心,我的職責就是維護服務例項,並不需要去檢索服務
fetch-registry: false
service-url:
#設定與Eureka server互動的地址查詢服務和註冊服務都需要依賴這個地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

新增主啟動類

package com.dance.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 { public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args);
} }

5.測試執行EurekaMain7001,瀏覽器輸入http://localhost:7001/回車,會檢視到Spring Eureka服務主頁。

服務端搭建成功

提供者(支付微服務8001工程)註冊服務到EurekaServer

修改提供者POM.xml

新增spring-cloud-starter-netflix-eureka-client依賴

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

修改yml配置

eureka:
client:
#表示是否將自己註冊進Eurekaserver預設為true。
register-with-eureka: true
#是否從EurekaServer抓取已有的註冊資訊,預設為true。單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka

修改主啟動類

@EnableEurekaClient//<-----新增該註解

重啟提供者服務,並檢視Eureka控制檯

提供者註冊成功

Eureka自我保護機制

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARELESSER THAN THRESHOLD AND HENCFT ARE NOT BEING EXPIRED JUST TO BE SAFE.

緊急情況!EUREKA可能錯誤地聲稱例項在沒有啟動的情況下啟動了。續訂小於閾值,因此例項不會為了安全而過期。

消費者(訂單微服務80工程)註冊服務到EurekaServer

修改消費者POM.xml

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

修改yml配置

eureka:
client:
#表示是否將自己註冊進Eurekaserver預設為true。
register-with-eureka: true
#是否從EurekaServer抓取已有的註冊資訊,預設為true。單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka

修改主啟動類

@EnableEurekaClient//<--- 新增該標籤

測試

  • 啟動cloud-provider-payment8001、cloud-eureka-server7001和cloud-consumer-order80這三工程。
  • 瀏覽器輸入 http://localhost:7001 , 在主頁的Instances currently registered with Eureka將會看到cloud-provider-payment8001、cloud-consumer-order80兩個工程名。

注意,application.yml配置中層次縮排和空格,兩者不能少,否則,會丟擲異常Failed to bind properties under 'eureka.client.service-url' to java.util.Map <java.lang.String, java.lang.String>。

Eureka叢集原理說明

Eureka叢集原理說明

服務註冊:將服務資訊註冊進註冊中心

服務發現:從註冊中心上獲取服務資訊

實質:存key服務命取value閉用地址

  1. 先啟動eureka注主冊中心
  2. 啟動服務提供者payment支付服務
  3. 支付服務啟動後會把自身資訊(比服務地址L以別名方式注朋進eureka
  4. 消費者order服務在需要呼叫介面時,使用服務別名去註冊中心獲取實際的RPC遠端呼叫地址
  5. 消去者導呼叫地址後,底屋實際是利用HttpClient技術實現遠端呼叫
  6. 消費者實癸導服務地址後會快取在本地jvm記憶體中,預設每間隔30秒更新—次服務呼叫地址

問題:微服務RPC遠端服務呼叫最核心的是什麼

高可用:試想你的註冊中心只有一個only one,萬一它出故障了,會導致整個為服務環境不可用。

解決辦法:搭建Eureka註冊中心叢集,實現負載均衡+故障容錯。

互相註冊,相互守望。

Eureka叢集環境搭建

搭建7002

建立cloud-eureka-server7002模組,參考搭建EurekaServer

修改Hosts檔案

  • 找到C:\Windows\System32\drivers\etc路徑下的hosts檔案,修改對映配置新增進hosts檔案
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com

修改7001配置檔案

server:
port: 7001 eureka:
instance:
hostname: eureka7001.com #eureka服務端的例項名稱
client:
register-with-eureka: false #false表示不向註冊中心註冊自己。
fetch-registry: false #false表示自己端就是註冊中心,我的職責就是維護服務例項,並不需要去檢索服務
service-url:
#叢集指向其它eureka
defaultZone: http://eureka7002.com:7002/eureka/
#單機就是7001自己
#defaultZone: http://eureka7001.com:7001/eureka/

修改7002配置檔案

server:
port: 7002 eureka:
instance:
hostname: eureka7002.com #eureka服務端的例項名稱
client:
register-with-eureka: false #false表示不向註冊中心註冊自己。
fetch-registry: false #false表示自己端就是註冊中心,我的職責就是維護服務例項,並不需要去檢索服務
service-url:
#叢集指向其它eureka
defaultZone: http://eureka7001.com:7001/eureka/
#單機就是7002自己
#defaultZone: http://eureka7002.com:7002/eureka/

測試叢集

啟動7002和7001,先停掉8001和80

通過域名訪問7001,可以看到它指向了7002

http://eureka7001.com:7001/

通過域名訪問7002.可以看到它指向了7001

http://eureka7002.com:7002

測試完成,叢集搭建成功!

提供者(支付微服務8001工程)註冊服務到Eureka叢集

修改yml配置

#將單一地址修改為多地址通過逗號分割
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka

消費者(訂單微服務80工程)註冊服務到Eureka叢集

修改yml配置

#將單一地址修改為多地址通過逗號分割
defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka  

測試註冊服務

  1. 啟動叢集7001和7002
  2. 啟動提供者和消費者
  3. 訪問Eureka檢視

7001註冊成功

7002註冊成功

提供者叢集搭建

參考cloud-provider-payment8001搭建cloud-provider-payment8002,應為在微服務當中為了解決單點故障問題,提供者應該是多臺而不是一臺

修改yml配置

server:
port: 8002

測試

  1. 啟動Eureka叢集
  2. 啟動提供者叢集8001 8002
  3. 訪問Eureka測試

7001叢集註冊成功

7002叢集註冊成功

消費者通過別名呼叫提供者叢集

修改消費者Controller

//public static final String PAYMENT_URL = "http://localhost:8001";

// 將指定的地址修改為服務別名 服務別名從Eureka控制檯檢視

public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

修改RestTemplate配置

@Bean
// 應為是叢集提供服務,所以需要本地負載
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}

測試呼叫

重啟80工程,第一次呼叫 8002 提供者提供服務

重新整理幾次可以看到 8001 提供者提供服務

測試成功

actuator微服務資訊完善

主機名稱:服務名稱修改(也就是將IP地址,換成可讀性高的名字)

修改cloud-provider-payment8001,cloud-provider-payment8002

修改部分 - YML - eureka.instance.instance-id

也就是修復Eureka展示的問題

修改提供者8001配置

eureka:
instance:
instance-id: cloud-payment-service8001

修改提供者8002配置

eureka:
instance:
instance-id: cloud-payment-service8002

重啟測試

可以了,但是現在滑鼠懸浮在上面左下角出現的還是域名,而不是IP

訪問資訊有IP資訊提示

(就是將滑鼠指標移至payment8001,payment8002名下,會有IP地址提示)

修改8001和8002

eureka:
instance:
prefer-ip-address: true #新增此處

服務發現 Discovery

對於註冊進eureka裡面的微服務,可以通過服務發現來獲得該服務的資訊

修改8001的Controller

@RestController
@Slf4j
public class PaymentController { .................. @Resource
private DiscoveryClient discoveryClient; @GetMapping("/payment/discovery")
public Object discovery(){
// 獲取描述
String description = discoveryClient.description();
log.info(description);
// 獲取所有服務別名
List<String> services = discoveryClient.getServices();
services.forEach(System.out::println);
// 根據別名獲取具體提供者資訊
services.forEach(x -> {
List<ServiceInstance> instances = discoveryClient.getInstances(x);
for (ServiceInstance instance : instances) {
log.info("{instanceId:"+instance.getInstanceId()+",scheme:"+instance.getScheme()+
",serviceId:"+instance.getServiceId()+",host:"+instance.getHost()+
",port:"+instance.getPort()+",uri:"+instance.getUri()+
",metadata:"+instance.getMetadata()+"}");
}
});
return services;
} .......................
}

修改主啟動類

@EnableDiscoveryClient//新增該註解

測試

重啟8001,8002提供者

測試

http://localhost:8001/payment/discovery

前臺返回:

後端列印:

2021-08-22 01:12:33.434  INFO [cloud-payment-service,912fc71f3a7277d4,912fc71f3a7277d4,true] 8088 --- [nio-8001-exec-2] c.d.s.controller.PaymentController       : Spring Cloud Eureka Discovery Client
cloud-payment-service
cloud-order-service
2021-08-22 01:12:33.442 INFO [cloud-payment-service,912fc71f3a7277d4,912fc71f3a7277d4,true] 8088 --- [nio-8001-exec-2] c.d.s.controller.PaymentController : {instanceId:cloud-payment-service8002,scheme:null,serviceId:CLOUD-PAYMENT-SERVICE,host:192.168.0.101,port:8002,uri:http://192.168.0.101:8002,metadata:{management.port=8002}}
2021-08-22 01:12:33.442 INFO [cloud-payment-service,912fc71f3a7277d4,912fc71f3a7277d4,true] 8088 --- [nio-8001-exec-2] c.d.s.controller.PaymentController : {instanceId:ZB-PF2P9QVH.360buyAD.local:cloud-order-service:80,scheme:null,serviceId:CLOUD-ORDER-SERVICE,host:ZB-PF2P9QVH.360buyAD.local,port:80,uri:http://ZB-PF2P9QVH.360buyAD.local:80,metadata:{management.port=80}}

Eureka 自我保護機制

概述

保護模式主要用於一組客戶端和Eureka Server之間存在網路分割槽場景下的保護。一旦進入保護模式,Eureka Server將會嘗試保護其服務登錄檔中的資訊,不再刪除服務登錄檔中的資料,也就是不會登出任何微服務。

如果在Eureka Server的首頁看到以下這段提示,則說明Eureka進入了保護模式:

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THANTHRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUSTTO BE SAFE

導致原因

一句話:某時刻某一個微服務不可用了,Eureka不會立刻清理,依舊會對該微服務的資訊進行儲存。

屬於CAP裡面的AP分支。

為什麼會產生Eureka自我保護機制

為了EurekaClient可以正常執行,防止與EurekaServer網路不通情況下,EurekaServer不會立刻將EurekaClient服務剔除

什麼是自我保護模式

預設情況下,如果EurekaServer在一定時間內沒有接收到某個微服務例項的心跳,EurekaServer將會登出該例項(預設90秒)。但是當網路分割槽故障發生(延時、卡頓、擁擠)時,微服務與EurekaServer之間無法正常通訊,以上行為可能變得非常危險了——因為微服務本身其實是健康的,此時本不應該登出這個微服務。Eureka通過“自我保護模式”來解決這個問題——當EurekaServer節點在短時間內丟失過多客戶端時(可能發生了網路分割槽故障),那麼這個節點就會進入自我保護模式。

自我保護機制∶預設情況下EurekaClient定時向EurekaServer端傳送心跳包,如果EurekaServer端在一定時間內(預設90秒)沒有收到EurekaClient傳送的心跳包,便會直接從服務註冊列表中剔除該服務,但是在短時間( 90秒中)內丟失了大量的服務例項心跳,這時候Eurekaserver會開啟自我保護機制,不會剔除該服務(該現象可能出現在如果網路不通但是EurekaClient為出現宕機,此時如果換做別的註冊中心如果一定時間內沒有收到心跳會將剔除該服務,這樣就出現了嚴重失誤,因為客戶端還能正常傳送心跳,只是網路延遲問題,而保護機制是為了解決此問題而產生的)。

在自我保護模式中,Eureka Server會保護服務登錄檔中的資訊,不再登出任何服務例項。

它的設計哲學就是寧可保留錯誤的服務註冊資訊,也不盲目登出任何可能健康的服務例項。一句話講解:好死不如賴活著。

綜上,自我保護模式是一種應對網路異常的安全保護措施。它的架構哲學是寧可同時保留所有微服務(健康的微服務和不健康的微服務都會保留)也不盲目登出任何健康的微服務。使用自我保護模式,可以讓Eureka叢集更加的健壯、穩定。

怎麼禁止自我保護

在7001處設定關閉自我保護機制

預設,自我保護機制是開啟的

使用eureka.server.enable-self-preservation = false可以禁用自我保護模式

eureka:
...
server:
#關閉自我保護機制,保證不可用服務被及時踢除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000

關閉效果:

spring-eureka主頁會顯示出一句:

THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

在8001設定心跳檢測和續約時間

  • 預設心跳時間為30秒

    • eureka.instance.lease-renewal-interval-in-seconds=30
    • 續約時間為90秒
      • eureka.instance.lease-expiration-duration-in-seconds=90
eureka:
...
instance:
instance-id: payment8001
prefer-ip-address: true
#心跳檢測與續約時間
#開發時沒置小些,保證服務關閉後註冊中心能即使剔除服務
#Eureka客戶端向服務端傳送心跳的時間間隔,單位為秒(預設是30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服務端在收到最後一次心跳後等待時間上限,單位為秒(預設是90秒),超時將剔除服務
lease-expiration-duration-in-seconds: 2

測試

  • 重啟Eureka叢集
  • 重啟提供者叢集
  • 訪問控制檯檢視

因為修改的是7001,所以檢視7001控制檯

註冊成功

現在停止8001提供者,重新整理7001控制檯

已經被踢出了

7002也踢出了8001

ok 到這裡Eureka就寫完啦,完結撒花~,這些知識應該夠日常開發用了,而且用不用Eureka還另說,為啥不用凡是關注這一塊的差不多都知道

作者:彼岸舞

時間:2021\08\22

內容關於:Spring Cloud H版

本文屬於作者原創,未經允許,禁止轉發