提高微服務效能的最佳做法

區域 ID

REGION_ID 是 Google 根據您在建立應用程式時選取的地區所指派的簡寫代碼。雖然某些區域 ID 可能看起來與常用的國家/地區代碼相似,但此代碼並非對應國家/地區或省份。如果是 2020 年 2 月後建立的應用程式,App Engine 網址會包含 REGION_ID.r。如果是在此日期之前建立的現有應用程式,網址中則可選擇加入地區 ID。

進一步瞭解區域 ID

軟體開發作業的重點在於權衡利弊得失,微服務也不例外。您在程式碼部署和獨立運作的作業方面有所得,就必須以效能負擔做為代價。本節提供一些建議,說明您可以採取哪些步驟來盡量減少這種影響。

將 CRUD 作業轉換為微服務

微服務特別適合用於使用建立、擷取、更新、刪除 (CRUD) 模式存取的實體。使用這類實體時,通常一次只會使用一個實體 (例如使用者),而且通常一次只會執行一個 CRUD 動作。因此,您只需要單一微服務呼叫即可進行作業。尋找具有 CRUD 作業的實體,以及可在應用程式許多部分中使用的一組商業方法。這些實體是建立微服務的最佳候選項目。

提供批次處理 API

除了 CRUD 型式的 API 之外,您也可以透過提供批次處理 API 來為實體群組提供良好的微服務效能。例如,提供可接受一組使用者 ID 並傳回對應使用者字典的 API,而不只是發佈可擷取單一使用者的 GET API 方法:

要求:

/user-service/v1/?userId=ABC123&userId=DEF456&userId=GHI789

回應:

{
  "ABC123": {
    "userId": "ABC123",
    "firstName": "Jake",
    … },
  "DEF456": {
    "userId": "DEF456",
    "firstName": "Sue",
    … },
  "GHI789": {
    "userId": "GHI789",
    "firstName": "Ted",
    … }
}

App Engine SDK 支援許多批次處理 API,例如透過單一遠端程序呼叫 (RPC) 從 Cloud Datastore 中擷取許多實體的功能,因此為這些類型的批次處理 API 提供服務會非常有效率。

使用非同步要求

一般來說,您必須與許多微服務互動才能建立回應。例如,您可能需要擷取已登入使用者的偏好設定及其公司詳細資料。這些資訊往往彼此獨立,因此您可以並行擷取這些資訊。App Engine SDK 中的 Urlfetch 資料庫支援非同步要求,因此您可以並行呼叫微服務。

以下 Python 程式碼範例直接透過 RPC 使用非同步要求

from google.appengine.api import urlfetch

preferences_rpc = urlfetch.create_rpc()
urlfetch.make_fetch_call(preferences_rpc,
                         'https://preferences-service-dot-my-app.uc.r.appspot.com/preferences-service/v1/?userId=ABC123')

company_rpc = urlfetch.create_rpc()
urlfetch.make_fetch_call(company_rpc,
                         'https://company-service-dot-my-app.uc.r.appspot.com/company-service/v3/?companyId=ACME')

 ### microservice requests are now occurring in parallel

try:
  preferences_response = preferences_rpc.get_result()  # blocks until response
  if preferences_response.status_code == 200:
    # deserialize JSON, or whatever is appropriate
  else:
    # handle error
except urlfetch.DownloadError:
  # timeout, or other transient error

try:
  company_response = company_rpc.get_result()  # blocks until response
  if company_response.status_code == 200:
    # deserialize JSON, or whatever is appropriate
  else:
    # handle error
except urlfetch.DownloadError:
  # timeout, or other transient error

並行作業往往與好的程式碼結構相悖,因為在實際的情境中,您經常得使用兩個類別來分別封裝偏好方法和公司方法。要在不破壞這類封裝的情況下使用非同步 Urlfetch 呼叫,是相當困難的事。App Engine Python SDK 的 NDB 套件中提供了一個不錯的解決方案:Tasklet。您可以使用 Tasklet 在程式碼中維持良好的封裝,同時提供機制來達成並行微服務呼叫。請注意,Tasklet 使用 futures 而非 RPC,但概念相似。

使用最短路徑

Urlfetch 的叫用方式會影響您所使用的基礎架構和路徑。如要使用效能最佳的路徑,請考慮以下建議:

使用 REGION_ID.r.appspot.com,而不要使用自訂網域
您在透過 Google 基礎架構進行轉送時,使用自訂網域會導致路徑改變。由於微服務呼叫是在內部進行,因此如果您使用 https://PROJECT_ID.REGION_ID.r.appspot.com,可以輕鬆進行呼叫,效能也會更佳。
follow_redirects 設為 False
在呼叫 Urlfetch 時明確設定 follow_redirects=False,這樣即可避免使用追蹤重新導向作業的大型服務。您的 API 端點應該不需要重新導向用戶端,因為它們是您自己的微服務,端點應只傳回 HTTP 200、400 以及 500 系列的回應。
建議您將服務集中在同一項專案中,而不要使用多項專案
您在建構微服務型應用程式時,使用多項專案可帶來許多優勢,不過如果效能是您的主要目標,則請在單一專案中使用服務。專案中的服務會託管在同一個資料中心,即使 Google 資料中心之間的網路總處理量很高,本地呼叫還是較快。

在執行安全防護機制期間避免往返通訊

使用涉及大量往返通訊來驗證呼叫 API 的安全性機制會對效能造成不良影響。例如,如果您的微服務必須回呼應用程式來驗證應用程式中的票證,那麼您就必須進行多次往返作業才能取得資料。

導入 OAuth2 可以使用重新整理憑證並在 Urlfetch 叫用之間快取存取憑證來分攤費用。不過,如果快取的存取憑證是儲存在 Memcache 中,您必須產生 Memcache 負擔才能擷取該組憑證。如要避免這項負擔,您可以將存取憑證快取至執行個體的記憶體中,但您仍會經常遇到 OAuth2 活動,因為每個新的執行個體都會與存取憑證交涉。請記住,App Engine 執行個體會經常啟動與停止。混用 Memcache 和執行個體快取將有助於緩解這個問題,但您的解決方案會開始變得更複雜。

另一種可提供良好效能的方法是在微服務之間共用密鑰憑證,例如以自訂 HTTP 標頭的形式傳輸資料。在這種方法中,每項微服務都可以為每位呼叫者提供一組專屬憑證。一般來說,共用密鑰可能會對安全性實作造成影響,但由於所有微服務都位於同一個應用程式,在效能提升的情況下,問題的嚴重程度就較低。如果您使用了共用密鑰,微服務僅須進行字串比對,基本上是將傳入的密鑰與記憶體中的字典相互比較,因此只需要強制執行簡易型安全措施。

如果您的所有微服務都位於 App Engine,您也可以查看傳入的 X-Appengine-Inbound-Appid 標頭。您向另一項 App Engine 專案發出要求時,Urlfetch 基礎架構會新增這個標頭,這個標頭無法由外部第三方設定。視安全性需求而定,您的微服務可以檢查這個傳入的標頭,以便強制執行安全性政策。

追蹤微服務要求

建構微服務型應用程式時,您就會開始累積因連續的 Urlfetch 呼叫而產生的負擔。在這種情況下,您可以使用 Cloud Trace 來查看執行中的呼叫和產生負擔的位置。更重要的是,Cloud Trace 也能協助您找出依序叫用個別微服務的位置,讓您重新編寫程式碼,以並行方式執行這些擷取作業。

您在單一專案中使用多項服務時,Cloud Trace 有一項功能便能派上用場。專案中微服務的不同服務之間進行呼叫時,Cloud Trace 會將所有呼叫合併為單一呼叫圖形,方便您透過圖表將整項端對端要求轉換為單一追蹤項目。

Google Cloud Trace 螢幕擷圖

請注意,在上方範例中,對 pref-serviceuser-service 發出的呼叫是使用非同步 Urlfetch 來並行執行,因此遠端程序呼叫 (RPC) 在視覺化圖表中會變為亂碼。不過,以診斷延遲情況來說,這仍然是相當實用的工具。

後續步驟