SpringColud Eureka的服務註冊與發現

牧小農的夏天發表於2020-06-14

一、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功能

  1. 註冊:每個微服務啟動時,將自己的網路地址等資訊註冊到註冊中心,註冊中心會儲存(記憶體中)這些資訊。
  2. 獲取服務登錄檔:服務消費者從註冊中心,查詢服務提供者的網路地址,並使用該地址呼叫服務提供者,為了避免每次都查登錄檔資訊,所以client會定時去server拉取登錄檔資訊到快取到client本地。
  3. 心跳:各個微服務與註冊中心通過某種機制(心跳)通訊,若註冊中心長時間和服務間沒有通訊,就會登出該例項。
  4. 呼叫:實際的服務呼叫,通過登錄檔,解析服務名和具體地址的對應關係,找到具體服務的地址,進行實際呼叫。

1.2.2 server註冊中心功能

  1. 服務登錄檔:記錄各個微服務資訊,例如服務名稱,ip,埠等。
    登錄檔提供 查詢API(查詢可用的微服務例項)和管理API(用於服務的註冊和登出)。
  2. 服務註冊與發現:註冊:將微服務資訊註冊到註冊中心。發現:查詢可用微服務列表及其網路地址。
  3. 服務檢查:定時檢測已註冊的服務,如發現某例項長時間無法訪問,就從登錄檔中移除。

二、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是非常有必要進行了解的,如果覺得文章對你有幫助,來個點贊支援吧,如果對文章有疑問或建議,歡迎討論留言,謝謝大家~

相關文章