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

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

將 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.my-app.appspot.com/preferences-service/v1/?userId=ABC123')

company_rpc = urlfetch.create_rpc()
urlfetch.make_fetch_call(company_rpc,
                         'https://company-service.my-app.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 是使用「Future」而非 RPC,但概念是類似的。

使用最短路徑

您可以根據叫用 Urlfetch 的方式來使用不同的基礎架構和路徑。為了使用效能最佳的路徑,請考慮以下建議:

使用 *.appspot.com,而非自訂網域
在透過 Google 基礎架構轉送時,自訂網域會導致使用不同的路徑。由於微服務呼叫是在內部進行,因此如果您使用 my-app.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) 在視覺化圖表中會出現亂碼。然而,這對於診斷延遲而言仍然是很有用的工具。

後續步驟

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Python 2 適用的 App Engine 標準環境