MicroProfile Health・服務健康嗎?

不曉得大家在開發 REST API 的時候,是否碰到過被反應服務出狀況,但是從 VM 監控上又看不出個所以然來,簡單的寫個 echo API 又不足以代表應用的實際負載程度,怎麼辦?
每個人應該會有不同的實作方法,首先要定義 API,然後實作各種檢查,最後一個一個匯總起來,最後透過 API 來判斷服務是否正常運作。

我相信大家也是這麼做的,這的確是很好的解決方案!但既然已經能自行解決,那麼 MicroProfile Health(後簡稱 MPHealth)是想幫助開發者解決什麼問題呢?
MPHealth 對開發者來說,主要是提供框架讓開發者可以省去定義 API 及開放 API 的困擾,只要專心在實作各種檢查就好。


存取服務健康 REST API

MPOpenAPI 一樣,部署應用啟動的同時,MPHealth 的機制已經啟動了,可以透過以下端點存取:

  • /health/ready => 服務是否準備就緒
  • /health/live => 服務是否正常運作
  • /health => 為了相容性而保留的舊版規格,會匯總 readylive 的內容

實際上透過瀏覽器去連結會取得以下的 JSON:

{
  "status": "UP",
  "checks": []
}

因為沒有實作任何檢查,所以預設情況下,取得的資料就是 UP 正常運作的狀態。

實作健康檢查

依照 MPHealth 的定義,健康分為兩種:

  1. Readiness => 服務是否準備就緒,對應回 /health/ready
  2. Liveness => 服務是否正常運作,對應回 /health/live

規格中,並沒有要求或建議各別該實作哪些檢查,而是提供了便利實作這些檢查的 @annotation 和 API。判斷及實作 ReadinessLiveness 該具備哪些檢查邏輯,因應各應用需求會有所不同,這些就是我們開發人員的工作啦!

使用 MPHealth 實作健康檢查很簡單,只要把握以下幾個重點:

  • @Readiness => 用來標註是用作檢查是否準備就緒
  • @Liveness => 用來標註是用作檢查是否正常運作
  • HealthCheck => 用來實作健康檢查邏輯的 interface
  • HealthCheckResponse => 健康檢查的回傳值

接下來,就來寫寫看!

// Java Code
@Readiness
@ApplicationScoped
public class MPHealthReadiness implements HealthCheck {

    @Override
    public HealthCheckResponse call() {
        return HealthCheckResponse.named("Environment")
            .withData("DB", true)
            .withData("DB-Connection", 10)
            .state(true)
            .build();
    }
}

是不是很簡單?
只要實作 HealthCheck,把邏輯填入,然後標註 @Readiness 就會被列入準備就緒檢查。回傳數值中可以透過 withData(key, value) 加入各種資料,依照需求填入對準備就緒檢查有必要的數值。開發者撰寫邏輯來判斷是否要通過檢查並填入 state(up)true 代表通過,若不需要判斷也可以直接使用 up()down() 來代表通過或失敗。

讓我們來看看呼叫 /health/ready 的結果:

{
  "status": "UP",
  "checks": [
    {
      "name": "Environment",
      "status": "UP",
      "data": {
        "DB-Connection": "10",
        "DB": "true"
      }
    }
  ]
}

這次因為有實作檢查,所以有數值了!內容就如同程式碼中所撰寫的呈現在結果之中。

在此說明幾個重點:

  • @Liveness 的用法和 @Readiness 完全相同
  • 所有標註為 @Readiness@Liveness 的檢查,其結果會個別被匯總到對應的 REST API
    • 開發者可以去規劃怎麼管理檢查實作邏輯
  • 不保證檢查被呼叫的順序,所以請確保單一檢查的完整性

一種檢查一個 class,好似沒什麼問題,保有了最佳的彈性,但是一個檢查一個 class 有時在管理上也不是那麼的方便。若是這種情況,MPHealth 也提供了另一種實作方法來達成。

@ApplicationScoped
public class MPHealthPractice {

    @Produces
    @Liveness
    public HealthCheck liveCheck1() {
        return () -> HealthCheckResponse.named("I am lived")
            .state(true)
            .build();
    }

    @Produces
    @Liveness
    public HealthCheck liveCheck2() {
        return new HealthCheck() {
            @Override
            public HealthCheckResponse call() {
                return HealthCheckResponse.named("I am also lived")
                    .state(true).build();
            }
        };
    }
}

用這種寫法就可以方便統一管理同一類型的數個檢查,實際上還是實作了 HealthCheck 再透過 CDI@Produces 提供給 MPHealth 框架。
至於要採用 lambdaanonymous inner class 來撰寫,就端看個人。

REST API Status Code

在這要建議大家若要使用 MPHealth,請務必參照其中的 Response Codes and status mappings

幫忙大家做個簡單的總結,若 HTTP Status 是:

  1. 200 => UP 正常,包含 payload
  2. 503 => DOWN 異常,包含 payload
  3. 500 => 無法判斷的異常,無 payload

若要自己透過 REST API 來確認伺服器狀態,最簡單的判斷方式就是 200 才是正常情況,接下來才考慮各數值的狀態。


文末,做個簡單的結語,

MPHealth 提供了一個極容易使用的機制讓開發者實作服務的健康檢查,同時 REST API 的包裝也被包含在此機制之中。若有監控服務的需求,可以考慮採用此框架簡化開發邏輯。由於是公開規格,若廠商支援也可以順利自動整合。為了更穩健的提供服務,有何道理不採用呢?

by Arren Ping