1. 程式人生 > 程式設計 >Spring Cloud 系列之註冊中心 Eureka詳解

Spring Cloud 系列之註冊中心 Eureka詳解

1.1 簡介

1.1.1 概述

  Netflix Eureka 是由 Netflix 開源的一款基於 REST 的服務發現元件,包括 Eureka Server 及 Eureka Client。2012 年 9 月在 GitHub 上釋出 1.1.2 版本,目前 Netflix 以宣佈閉源,所以市面上還是以 1.x 版本為主。Eureka 提供基於 REST 的服務,在叢集中主要用於服務管理。Eureka 提供了基於 Java 語言的客戶端元件,客戶端元件實現了負載均衡的功能,為業務元件的叢集部署創造了條件。使用該框架,可以將業務元件註冊到 Eureka 容器中,這些元件可進行叢集部署,Eureka 主要維護這些服務的列表並自動檢查它們的狀態。Spring Cloud Netflix Eureka 是 Pivotal 公司為了將 Netflix Eureka 整合於 Spring Cloud 生態系統提供的版本。

  Eureka 包含兩個元件:Eureka Server 和 Eureka Client, Eureka Server 提供服務註冊服務。各個微服務節點通過配置啟動後,會在 EurekaServer 中進行註冊,這樣 EurekaServer 中的服務登錄檔中將會儲存所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀看到。EurekaClient 通過註冊中心進行訪問。它是一個 Java 客戶端,用於簡化 Eureka Server 的互動,客戶端同時也具備一個內建的、使用輪詢(round-robin)負載演算法的負載均衡器。在應用啟動後,將會向 Eureka Server 傳送心跳(預設週期為30秒)。如果 Eureka Server 在多個心跳週期內沒有接收到某個節點的心跳,EurekaServer 將會從服務登錄檔中把這個服務節點移除(預設90秒)

在這裡插入圖片描述

1.1.2 原理圖

  一個簡單的 Eureka 叢集,需要一個 Eureka 伺服器、若干個服務提供者。我們可以將業務元件註冊到 Eureka 伺服器中,其他客戶端元件可以向伺服器獲取服務並且進行遠端呼叫。Eureka:就是服務註冊中心(可以是一個叢集),對外暴露自己的地址;提供者:啟動後向 Eureka 註冊自己資訊(地址,提供什麼服務);消費者:向 Eureka 訂閱服務,Eureka 會將對應服務的所有提供者地址列表傳送給消費者,並且定期更新;心跳(續約):提供者定期通過 http 方式向 Eureka 重新整理自己的狀態。

在這裡插入圖片描述

1.1.3 相關依賴

<!-- 需要確定 Spring Cloud 版本 -->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

1.2 搭建 EurekaServer

1.2.1 相關依賴

  現在都是子父工程,我們將子模組中都需要用的依賴放到父工程的 pom 檔案中

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <packaging>pom</packaging>
 <modules>
 <module>eureka</module>
 </modules>
 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.2.10.RELEASE</version>
 <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>com.software</groupId>
 <artifactId>spring-cloud</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>spring-cloud</name>
 <description>Demo project for Spring Boot</description>

 <properties>
 <java.version>1.8</java.version>
 <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
 </properties>

 <dependencies>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
 <!-- 演示就不使用資料庫了 -->
		<!--<dependency>-->
		<!--<groupId>org.springframework.boot</groupId>-->
		<!--<artifactId>spring-boot-starter-data-jpa</artifactId>-->
		<!--</dependency>-->
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <!-- H 版 Spring Cloud 將 server 與 client 分開了,需要匯入兩個座標 -->
 <dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 </dependency>
 </dependencies>

 <dependencyManagement>
 <dependencies>
  <dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-dependencies</artifactId>
  <version>${spring-cloud.version}</version>
  <type>pom</type>
  <scope>import</scope>
  </dependency>
 </dependencies>
 </dependencyManagement>

 <build>
 <plugins>
  <plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  </plugin>
 </plugins>
 </build>

</project>

1.2.2 宣告為 Eureka Server

  在 eureka 服務的啟動類上使用 @EnableEurekaServer,聲明當前應用為 Eureka 服務。

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description Eureka 啟動類
 */
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
 public static void main(String[] args) {
 SpringApplication.run(EurekaApplication.class,args);
 }
}

1.2.3 配置檔案

server:
 port: 8081

spring:
 application:
 name: eurekaServer # 應用名稱,在 Eureka 中作為 id 標識

eureka:
 client:
 register-with-eureka: false # 不註冊自己
 fetch-registry: false # 不拉取自己
 service-url:
 defaultZone: http://127.0.0.1:8081/eureka/ # EurekaServer 的地址,如果是叢集,需要加上其它 Server 的地址

1.2.4 啟動服務

  啟動服務訪問對應的埠就可以看到以下介面,現在是一個服務都沒有註冊上來。可以把 register-with-eurekafetch-registry 兩個配置取消就可以看到 eureka 自己了。

在這裡插入圖片描述

在這裡插入圖片描述




1.3 提供者

1.3.1 宣告為 Eureka Client

  在服務提供者啟動類中使用 @EnableDiscoveryClient,讓 Eureka 能夠發現,掃描到該服務。@EnableEurekaClient 註解也能實現但是該註解只支援 Eureka 作為註冊中心,@EnableDiscoveryClient 可以是其他註冊中心,建議使用 @EnableDiscoveryClient

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description 服務提供者啟動類
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
 public static void main(String[] args) {
 SpringApplication.run(ProviderApplication.class,args);
 }
}

1.3.2 配置檔案

server:
 port: 8082

spring:
 application:
 name: ProviderServer # 應用名稱,在 Eureka 中作為 id 標識

eureka:
 client:
 service-url:
 defaultZone: http://127.0.0.1:8081/eurake/

1.3.3 提供服務

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description
 */

@RestController
@RequestMapping("/provider")
public class ProviderController {

 @GetMapping("/get")
 public Object get() {
 return "你已經消費了";
 }
}

1.3.4 啟動服務

  啟動服務之後,會自動將自己註冊到 Eureka 中

在這裡插入圖片描述

1.4 消費者

1.4.1 宣告為 Eureka Client

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description 消費者啟動類
 */
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
 public static void main(String[] args) {
 SpringApplication.run(ConsumerApplication.class,args);
 }
	
	// 將 RestTemplate 交由容器管理
 @Bean
 public RestTemplate getRestTemplate() {
 return new RestTemplate();
 }
}

1.4.2 配置檔案

server:
 port: 8083

spring:
 application:
 name: ConsumerServer # 應用名稱,在 Eureka 中作為 id 標識

eureka:
 client:
 service-url:
 defaultZone: http://127.0.0.1:8081/eureka

1.4.3 消費服務

  我們之前使用 RestTemplate 需要自己寫 URI,這樣很不利於維護,而且容易出錯,現在只需要確定應用名稱,利用應用名稱從 Eureka 中就可以獲取到詳細資訊。

/**
 * Created with IntelliJ IDEA.
 *
 * @author [email protected]
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

 @Autowired
 private DiscoveryClient discoveryClient;


 @GetMapping("/go")
 public void go() {
 List<ServiceInstance> providerServer = discoveryClient.getInstances("ProviderServer");

 if (0 == providerServer.size()) {
  return;
 }

 ServiceInstance serviceInstance = providerServer.get(0);
 String host = serviceInstance.getHost();
 int port = serviceInstance.getPort();
 URI uri = serviceInstance.getUri();
 System.out.println("主機:" + host);
 System.out.println("埠:" + port);
 System.out.println("uri:" + uri);

 RestTemplate restTemplate = new RestTemplate();
 String str = restTemplate.getForObject(uri + "/provider/get",String.class);
 System.out.println(str);
 }
}

1.4.4 啟動服務

在這裡插入圖片描述

1.4.5 請求服務

在這裡插入圖片描述

1.4.6 執行流程

 ♞ 先啟動 eureka 註冊中心
 ♞ 啟動服務提供者 provider
 ♞ 服務提供者啟動後會把自身資訊(比如服務地址以別名方式註冊進 eureka)
 ♞ 消費者 consumer 服務在需要呼叫介面時,使用服務別名去註冊中心獲取實際的 RPC 遠端呼叫地址
 ♞ 消費者獲得呼叫地址後,底層實際是利用 HttpClient 技術實現遠端呼叫
 ♞ 消費者獲得服務地址後會快取在本地 jvm 記憶體中,預設每間隔 30 秒更新一次服務呼叫地址

1.5 補充配置

1.5.1 actuator 資訊完善

  我們現在的服務註冊到 Eureka 上面是沒有 ip 地址的,以後等服務搭建叢集是很不方便的,所以我們需要讓他顯示自己的 ip 地址;第二個就是服務名稱為主機 + 服務名 + 埠,這樣就暴露了主機名,我們可以指定顯示的名稱。

在這裡插入圖片描述

在配置檔案中新增如下配置,通過健康檢查(http://ip:port/actuator/health)檢視是否修改成功。

eureka:
 instance:
 # 例項名稱
 instance-id: consumer-01
 # 地址中顯示 ip 
 prefer-ip-address: true

在這裡插入圖片描述

1.5.2 自我保護機制 ☞ 概述

  我們可以看到 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 保護模式的開啟。一旦進入保護模式,Eureka Server 將會嘗試保護其服務登錄檔中的資訊,不再刪除服務登錄檔中的資料,也就是不會登出任何微服務。
  預設情況下,如果 Eureka Server 在一定時間內沒有接收到某個微服務例項的心跳,Eureka Server 將會登出該例項(預設90秒)。但是當網路分割槽故障發生(延時、卡頓、擁擠)時,微服務與 Eureka Server 之間無法正常通訊,以上行為可能變得非常危險了——因為微服務本身其實是健康的,此時本不應該登出這個微服務。為了防止 Eureka Client 可以正常執行,但是與 Eureka Server 網路不通情況下,Eureka Server 不會立刻將 Eureka Client 服務剔除。

☞ 關閉自我保護機制

  我們之前的 Eureka 截圖中可以看到 DESKTOP-GL7GS52:ConsumerServer:8083consumer-01 兩個同時存在,這明明是一個服務,修改完配置之後前面的沒有剔除,這就是因為自我保護機制打開了。

# Eureka Server 配置
eureka:
 server:
 # 關閉自我保護模式,預設為開啟
 enable-self-preservation: false 
 # 續期時間,即掃描失效服務的間隔時間 
 eviction-interval-timer-in-ms: 5000 


# Eureka Client 配置
eureka:
 instance:
 # Eureka Client 給 Eureka Server 傳送心跳的時間間隔,預設 30 單位是 s
 lease-renewal-interval-in-seconds: 1
 # Eureka Server 最後一次收到心跳的等待上限,超時剔除服務,預設 90 單位是 s
 lease-expiration-duration-in-seconds: 2

1.6 Eureka 高可用

1.6.1 Eureka 叢集搭建

  在之前的單體中我們的埠是隨意的,但是搭建叢集我們需要對埠進行規劃,例如將 808X 埠作為 Eureka 叢集的埠。先來看下配置有什麼區別,起初我們是將自己註冊到自己上,現在我們需要將自己註冊到其他 Eureka 上,有多個則用 , 隔開。

server:
 port: 8081

spring:
 application:
 name: eurekaServer

eureka:
 client:
 register-with-eureka: false
 fetch-registry: false
 service-url:
 defaultZone: http://127.0.0.1:8082/eureka
 server:
 enable-self-preservation: false
 eviction-interval-timer-in-ms: 5000
server:
 port: 8082

spring:
 application:
 name: eurekaServer_back

eureka:
 client:
 register-with-eureka: false
 fetch-registry: false
 service-url:
 defaultZone: http://127.0.0.1:8081/eureka
 server:
 enable-self-preservation: false
 eviction-interval-timer-in-ms: 5000

在這裡插入圖片描述
在這裡插入圖片描述

1.6.2 Privoder 叢集

  服務提供者的叢集配置了多個 Eureka 地址,會將自己同時註冊到多個 Eureka 上,除了配置檔案以外其他的服務程式碼完全一致,也可以加以區分是哪個提供的服務。需要注意的是 Eureka 叢集的應用名稱可以不一致甚至不寫,但是服務提供者的應用名稱必須保持一致,否則會被認為不是一個服務。

server:
 port: 8091

spring:
 application:
 name: ProviderServer # 應用名稱,在 Eureka 中作為 id 標識

eureka:
 client:
 service-url:
 defaultZone: http://127.0.0.1:8081/eureka,http://127.0.0.1:8082/eureka
 instance:
 instance-id: provider-prim
 prefer-ip-address: true
 lease-renewal-interval-in-seconds: 1
 lease-expiration-duration-in-seconds: 2
server:
 port: 8092

spring:
 application:
 name: ProviderServer

eureka:
 client:
 service-url:
 defaultZone: http://127.0.0.1:8081/eureka,http://127.0.0.1:8082/eureka
 instance:
 instance-id: provider-back
 prefer-ip-address: true
 lease-renewal-interval-in-seconds: 1
 lease-expiration-duration-in-seconds: 2

在這裡插入圖片描述

1.6.3 遠端呼叫

/**
 * Created with IntelliJ IDEA.
 *
 * @author Demo_Null
 * @date 2020/10/29
 * @description
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

 @Autowired
 private DiscoveryClient discoveryClient;


 @GetMapping("/go")
 public void go() {
 List<ServiceInstance> providerServer = discoveryClient.getInstances("ProviderServer");

 if (0 == providerServer.size()) {
  return;
 }

 RestTemplate restTemplate = new RestTemplate();

 for (ServiceInstance instance : providerServer) {
  System.out.print(instance.getUri() + "---");
  String url = instance.getUri() + "/provider/get";
  System.out.println(restTemplate.getForObject(url,String.class));
 }
 }
}

在這裡插入圖片描述

  咱們可以使用服務發現 DiscoveryClient 來獲取服務資訊,但是無法自動選擇使用那個服務,這裡就涉及到 Ribbon 負載均衡了。我們可以將 RestTemplate 交由 Ioc 管理,在注入時使用 @LoadBalanced 註解進行負載均衡。

☞ 原始碼

到此這篇關於Spring Cloud 系列之註冊中心 Eureka的文章就介紹到這了,更多相關Spring Cloud 註冊中心Eureka內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!