(十六) 跟我學習SpringCloud-Eureka的REST API及API擴展

本節我們講解了一些經常用到的配置信息及 Eureka 的 REST API,通過 API 可以做一些擴展。

Eureka REST API

Eureka 作爲註冊中心,其本質是存儲了每個客戶端的註冊信息,Ribbon 在轉發的時候會獲取註冊中心的服務列表,然後根據對應的路由規則來選擇一個服務給 Feign 來進行調用。如果我們不是 Spring Cloud 技術選型,也想用 Eureka,可以嗎?完全可以。

如果不是 Spring Cloud 技術棧,筆者推薦用 Zookeeper,這樣會方便些,當然用 Eureka 也是可以的,這樣的話就會涉及如何註冊信息、如何獲取註冊信息等操作。其實 Eureka 也考慮到了這點,提供了很多 REST 接口來給我們調用。

我們舉一個比較有用的案例來說明,比如對 Nginx 動態進行 upstream 的配置。

推薦分佈式架構源碼

在架構變成微服務之後,微服務是沒有依賴的,可以獨立部署,端口也可以隨機分配,反正會註冊到註冊中心裏面,調用方也無須關心提供方的 IP 和 Port,這些都可以從註冊中心拿到。

但是有一個問題:API 網關的部署能這樣嗎?API 網關大部分會用 Nginx 作爲負載,那麼 Nginx 就必須知道 API 網關有哪幾個節點,這樣網關服務就不能隨便啓動了,需要固定。

當然網關是不會經常變動的,也不會經常發佈,這樣其實也沒什麼大問題,唯一不好的就是不能自動擴容了。

其實利用 Eureka 提供的 API 我們可以獲取某個服務的實例信息,也就是說我們可以根據 Eureka 中的數據來動態配置 Nginx 的 upstream。

這樣就可以做到網關的自動部署和擴容了。網上也有很多的方案,結合 Lua 腳本來做,或者自己寫 Sheel 腳本都可以。

下面舉例說明如何獲取 Eureka 中註冊的信息。具體的接口信息請查看官方文檔“https://github.com/Netflix/eureka/wiki/Eureka-REST-operations“。

獲取某個服務的註冊信息,可以直接 GET 請求:http://localhost:8761/eureka/apps/eureka-client-user-service。其中,eureka-client-user-service 是應用名稱,也就是 spring.application.name。

在瀏覽器中,數據的顯示格式默認是 XML 格式的,如圖 1 所示。

 

如果想返回 Json數據的格式,可以用一些接口測試工具來請求,比如 Postman,在請求頭中添加下面兩行代碼即可。

Content-Type:application/json Accept:application/json

如果 Eureka 開啓了認證,記得添加認證信息,用戶名和密碼必須是 Base64 編碼過的 Authorization:Basic 用戶名:密碼,其餘的接口就不做過多講解了,大家可以自己去嘗試。Postman 直接支持了 Basic 認證,將選項從 Headers 切換到 Authorization,選擇認證方式爲 Basic Auth 就可以填寫用戶信息了。

填寫完之後,直接發起請求就可以了。我們切換到 Headers 選項中,就可以看到請求頭中已經多了一個 Authorization 頭。

元數據使用

Eureka 的元數據有兩種類型,分別是框架定好了的標準元數據和用戶自定義元數據。標準元數據指的是主機名、IP 地址、端口號、狀態頁和健康檢查等信息,這些信息都會被髮布在服務註冊表中,用於服務之間的調用。自定義元數據可以使用 eureka.instance.metadataMap 進行配置。

自定義元數據說得通俗點就是自定義配置,我們可以爲每個 Eureka Client 定義一些屬於自己的配置,這個配置不會影響 Eureka 的功能。

自定義元數據可以用來做一些擴展信息,比如灰度發佈之類的功能,可以用元數據來存儲灰度發佈的狀態數據,Ribbon 轉發的時候就可以根據服務的元數據來做一些處理。當不需要灰度發佈的時候可以調用 Eureka 提供的 REST API 將元數據清除掉。

下面我們來自定義一個簡單的元數據,在屬性文件中配置如下:

eureka.instance.metadataMap.biancheng=zhangsan

上述代碼定義了一個 key 爲 biancheng 的配置,value 是 zhangsan。重啓服務,然後通過 Eureka 提供的 REST API 來查看剛剛配置的元數據是否已經存在於 Eureka 中,如圖 2 所示。

EurekaClient 使用

當我們的項目中集成了 Eureka 之後,可以通過 EurekaClient 來獲取一些我們想要的數據,比如剛剛上面講的元數據。我們就可以直接通過 EurekaClient 來獲取(代碼如下所示),不用再去調用 Eureka 提供的 REST API。

@Autowired
private EurekaClient eurekaClient;
@GetMapping("/article/infos")
public Object serviceUrl() {
    return eurekaClient.getInstancesByVipAddress( "eureka-client-user-service", false);
}

通過 PostMan 來調用接口看看有沒有返回我們想要的數據。這時我們會發現,通過 EurekaClient 獲取的數據跟我們自己去掉 API 獲取的數據是一樣的,從使用角度來說前者比較方便。

除了使用 EurekaClient,還可以使用 DiscoveryClient(代碼如下所示),這個不是 Feign 自帶的,是 Spring Cloud 重新封裝的,類的路徑爲 org.springframework.cloud.client.discovery.DiscoveryClient。

@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/article/infos")
public Object serviceUrl() {
    return discoveryClient.getInstances("eureka-client-user-service");
}

健康檢查

默認情況下,Eureka 客戶端是使用心跳和服務端通信來判斷客戶端是否存活,在某些場景下,比如 MongoDB 出現了異常,但你的應用進程還是存在的,這就意味着應用可以繼續通過心跳上報,保持應用自己的信息在 Eureka 中不被剔除掉。

Spring Boot Actuator 提供了 /actuator/health 端點,該端點可展示應用程序的健康信息,當 MongoDB 異常時,/actuator/health 端點的狀態會變成 DOWN,由於應用本身確實處於存活狀態,但是 MongoDB 的異常會影響某些功能,當請求到達應用之後會發生操作失敗的情況。

在這種情況下,我們希望可以將健康信息傳遞給 Eureka 服務端。這樣 Eureka 中就能及時將應用的實例信息下線,隔離正常請求,防止出錯。通過配置如下內容開啓健康檢查:

eureka.client.healthcheck.enabled=true

我們可以通過擴展健康檢查的端點來模擬異常情況,定義一個擴展端點,將狀態設置爲 DOWN,代碼如下所示。

@Component
public class CustomHealthIndicator extends AbstractHealthIndicator {

    @Override
    protected void doHealthCheck(Builder builder) throws Exception {
        builder.down().withDetail("status", false);
    }
}

擴展好後我們訪問 /actuator/health 可以看到當前的狀態是 DOWN,如圖 3 所示。

Eureka 中的狀態是 UP,這種情況下請求還是能轉發到這個服務中,下面我們開啓監控檢查,再次查看 Eureka 中的狀態,發現狀態變爲 DOWN(1)。

服務上下線監控

在某些特定的需求下,我們需要對服務的上下線進行監控,上線或下線都進行郵件通知,Eureka 中提供了事件監聽的方式來擴展。

目前支持的事件如下:

  • EurekaInstanceCanceledEvent 服務下線事件。
  • EurekaInstanceRegisteredEvent 服務註冊事件。
  • EurekaInstanceRenewedEvent 服務續約事件。
  • EurekaRegistryAvailableEvent Eureka 註冊中心啓動事件。
  • EurekaServerStartedEvent Eureka Server 啓動事件。


基於 Eureka 提供的事件機制,可以監控服務的上下線過程,在過程發生中可以發送郵件來進行通知。下面代碼只是演示了監控的過程,並未發送郵件。

@Component
public class EurekaStateChangeListener {

    @EventListener
    public void listen(EurekaInstanceCanceledEvent event) {
        System.err.println(event.getServerId() + "\t" + event.getAppName() + " 服務下線 ");
    }

    @EventListener
    public void listen(EurekaInstanceRegisteredEvent event) {
        InstanceInfo instanceInfo = event.getInstanceInfo();
        System.err.println(instanceInfo.getAppName() + " 進行註冊 ");
    }

    @EventListener
    public void listen(EurekaInstanceRenewedEvent event) {
        System.err.println(event.getServerId() + "\t" + event.getAppName() + " 服務進行續約 ");
    }

    @EventListener
    public void listen(EurekaRegistryAvailableEvent event) {
        System.err.println(" 註冊中心啓動 ");
    }

    @EventListener
    public void listen(EurekaServerStartedEvent event) {
        System.err.println("Eureka Server啓動 ");
    }
}

注意:在 Eureka 集羣環境下,每個節點都會觸發事件,這個時候需要控制下發送通知的行爲,不控制的話每個節點都會發送通知。

推薦分佈式架構源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章