適用於:所有 API 管理層
Azure API 管理服務內建支援使用資源 URL 作為索引鍵進行 HTTP 回應快取。 您可以使用使用 vary-by 屬性的要求標頭來修改金鑰。 此技術對於快取整個 HTTP 回應(也稱為表示)很有用,但有時僅快取表示的一部分也很有用。
cache-lookup-value 和 cache-store-value 原則可讓您從原則定義內儲存和擷取任意資料片段。 此功能也會為 傳送要求 原則增加價值,因為您可以快取來自外部服務的回應。
Architecture
API 管理服務的個別租用戶內部資料會共用快取,因此當您擴充為多個單位時,仍可存取相同的快取資料。 不過,使用多區域部署時,在每個區域內有獨立的快取。 重要的是不要將快取視為資料存放區,因為這是某些資訊的唯一來源。 如果您這麼做,之後又決定要採用多區域部署,擁有旅遊使用者的客戶可能會失去該快取資料的存取權。
備註
內部快取無法在 Azure API 管理 的 取用 層中使用。 您可以改 用外部 Redis 相容快取 。 外部快取可讓所有層級中的 API 管理 執行個體提供更大的快取控制和彈性。
片段快取
在某些情況下,傳回的回應包含某些資料,而這些資料的判斷成本很高。 然而,數據在合理的時間範圍內保持新鮮。 例如,假設航空公司建置的服務提供與航班預訂、航班狀態等相關的資訊。 如果用戶是航空公司積分計劃的會員,他們還將獲得與其當前狀態和累積里程相關的信息。 此使用者相關資訊可能會儲存在不同的系統中,但最好將其包含在傳回的有關航班狀態和預訂的回應中。 您可以使用一種叫做片段快取的過程,將這些資料納入系統中。 可以使用某種編碼標記從原始伺服器傳回主要表示法,用來指出要插入使用者相關資訊的位置。
請考慮來自後端 API 的下列 JSON 回應。
{
"airline" : "Air Canada",
"flightno" : "871",
"status" : "ontime",
"gate" : "B40",
"terminal" : "2A",
"userprofile" : "$userprofile$"
}
在 /userprofile/{userid} 的次要資源看起來像,
{ "username" : "Bob Smith", "Status" : "Gold" }
若要判斷要包含的適當使用者資訊,API 管理 必須識別使用者是誰。 此機制取決於實作。 下列範例使用 Subject 權杖的 JWT 宣告。
<set-variable
name="enduserid"
value="@(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ')[1].AsJwt()?.Subject)" />
API 管理 會 enduserid 將值儲存在內容變數中,以供稍後使用。 下一步是確定先前的請求是否已經檢索了使用者資訊並將其儲存在快取中。 為此,API 管理 會使用 cache-lookup-value 原則。
<cache-lookup-value
key="@("userprofile-" + context.Variables["enduserid"])"
variable-name="userprofile" />
<rate-limit calls="10" renewal-period="60" />
如果快取中沒有對應於索引鍵值的項目,則不會建立任何 userprofile 內容變數。 API 管理 會使用控制流程原則檢查 choose 查閱是否成功。
<choose>
<when condition="@(!context.Variables.ContainsKey("userprofile"))">
<!-- If the userprofile context variable doesn’t exist, make an HTTP request to retrieve it. -->
</when>
</choose>
如果內容變數不存在,則 userprofile API 管理 必須提出 HTTP 要求才能擷取它。
<send-request
mode="new"
response-variable-name="userprofileresponse"
timeout="10"
ignore-error="true">
<!-- Build a URL that points to the profile for the current end-user -->
<set-url>@(new Uri(new Uri("https://apimairlineapi.azurewebsites.net/UserProfile/"),
(string)context.Variables["enduserid"]).AbsoluteUri)
</set-url>
<set-method>GET</set-method>
</send-request>
API 管理會使用 enduserid 來建構使用者設定檔資源的 URL。 一旦 API 管理 有回應,它就會從回應中提取本文文字,並將它儲存回內容變數中。
<set-variable
name="userprofile"
value="@(((IResponse)context.Variables["userprofileresponse"]).Body.As<string>())" />
若要避免 API 管理 在相同的使用者提出另一個要求時再次提出此 HTTP 要求,您可以指定使用者設定檔儲存在快取中。
<cache-store-value
key="@("userprofile-" + context.Variables["enduserid"])"
value="@((string)context.Variables["userprofile"])" duration="100000" />
API 管理使用與最初嘗試擷取值相同的金鑰,將該值儲存在快取中。 API 管理 選擇儲存值的持續時間,應該根據資訊變更的頻率,以及使用者對過期資訊的容忍程度。
請務必認識到,從快取擷取資訊仍然是非同步網路請求,可能將請求耗時增加數十毫秒。 當需要透過資料庫查詢或從多個後端彙總資訊時,判斷使用者設定檔資訊的時間會比從快取擷取資訊的時間更長,這時就會顯示出其優勢。
該過程的最後一步是使用用戶配置文件信息更新返回的響應。
<!-- Update response body with user profile-->
<find-and-replace
from='"$userprofile$"'
to="@((string)context.Variables["userprofile"])" />
您可以選擇將引號包含在標記中,這樣當替換沒有發生時,回應仍然是有效的 JSON。
結合這些步驟之後,最終結果是如下所示的原則。
<policies>
<inbound>
<!-- How you determine user identity is application dependent -->
<set-variable
name="enduserid"
value="@(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ')[1].AsJwt()?.Subject)" />
<!--Look for userprofile for this user in the cache -->
<cache-lookup-value
key="@("userprofile-" + context.Variables["enduserid"])"
variable-name="userprofile" />
<rate-limit calls="10" renewal-period="60" />
<!-- If API Management doesn’t find it in the cache, make a request for it and store it -->
<choose>
<when condition="@(!context.Variables.ContainsKey("userprofile"))">
<!-- Make HTTP request to get user profile -->
<send-request
mode="new"
response-variable-name="userprofileresponse"
timeout="10"
ignore-error="true">
<!-- Build a URL that points to the profile for the current end-user -->
<set-url>@(new Uri(new Uri("https://apimairlineapi.azurewebsites.net/UserProfile/"),(string)context.Variables["enduserid"]).AbsoluteUri)</set-url>
<set-method>GET</set-method>
</send-request>
<!-- Store response body in context variable -->
<set-variable
name="userprofile"
value="@(((IResponse)context.Variables["userprofileresponse"]).Body.As<string>())" />
<!-- Store result in cache -->
<cache-store-value
key="@("userprofile-" + context.Variables["enduserid"])"
value="@((string)context.Variables["userprofile"])"
duration="100000" />
</when>
</choose>
<base />
</inbound>
<outbound>
<!-- Update response body with user profile-->
<find-and-replace
from='"$userprofile$"'
to="@((string)context.Variables["userprofile"])" />
<base />
</outbound>
</policies>
此快取方法主要用於在伺服器端組成 HTML 的網站,以便轉譯為單一頁面。 在用戶端無法執行用戶端 HTTP 快取的 API 中,它也很有用,或者最好不要將該責任放在用戶端上。
這種相同的片段緩存也可以使用 Redis 緩存服務器在後端 Web 服務器上完成。 不過,當快取的片段來自與主要回應不同的後端時,使用 API 管理 服務來執行這項工作很有用。
透明版本控制
通常的做法是隨時支援 API 的多個不同實作版本。 例如,支援不同的環境 (開發、測試、生產等) 或支援舊版 API,讓 API 取用者有時間移轉至較新的版本。
作為處理多個版本的其中一種做法,是將取用者目前想使用的 API 版本儲存在取用者的設定檔資料中,並呼叫適當的後端 URL,而不需要用戶端開發人員將 URL 從 /v1/customers 變更為 /v2/customers。 若要判斷要呼叫特定用戶端的正確後端 URL,必須查詢一些組態資料。 當您快取此組態資料時,API 管理 可以將執行此查閱的效能損失降到最低。
第一步是確定用於配置所需版本的識別碼。 在此範例中,我們將版本與產品訂閱金鑰建立關聯。
<set-variable name="clientid" value="@(context.Subscription.Key)" />
然後,API 管理 會執行快取查閱,以查看是否已擷取所需的用戶端版本。
<cache-lookup-value
key="@("clientversion-" + context.Variables["clientid"])"
variable-name="clientversion" />
<rate-limit calls="10" renewal-period="60" />
接著,API 管理會確認該版本是否不在快取中。
<choose>
<when condition="@(!context.Variables.ContainsKey("clientversion"))">
如果 API 管理 找不到它,API 管理 會擷取它。
<send-request
mode="new"
response-variable-name="clientconfiguresponse"
timeout="10"
ignore-error="true">
<set-url>@(new Uri(new Uri(context.Api.ServiceUrl.ToString() + "api/ClientConfig/"),(string)context.Variables["clientid"]).AbsoluteUri)</set-url>
<set-method>GET</set-method>
</send-request>
從回應中擷取回應內文文字。
<set-variable
name="clientversion"
value="@(((IResponse)context.Variables["clientconfiguresponse"]).Body.As<string>())" />
將其儲存回快取中以備將來使用。
<cache-store-value
key="@("clientversion-" + context.Variables["clientid"])"
value="@((string)context.Variables["clientversion"])"
duration="100000" />
最後更新後端 URL 以選擇客戶端所需的服務版本。
<set-backend-service
base-url="@(context.Api.ServiceUrl.ToString() + "api/" + (string)context.Variables["clientversion"] + "/")" />
完整的政策如下:
<inbound>
<base />
<set-variable name="clientid" value="@(context.Subscription.Key)" />
<cache-lookup-value key="@("clientversion-" + context.Variables["clientid"])" variable-name="clientversion" />
<rate-limit calls="10" renewal-period="60" />
<!-- If API Management doesn’t find it in the cache, make a request for it and store it -->
<choose>
<when condition="@(!context.Variables.ContainsKey("clientversion"))">
<send-request mode="new" response-variable-name="clientconfiguresponse" timeout="10" ignore-error="true">
<set-url>@(new Uri(new Uri(context.Api.ServiceUrl.ToString() + "api/ClientConfig/"),(string)context.Variables["clientid"]).AbsoluteUri)</set-url>
<set-method>GET</set-method>
</send-request>
<!-- Store response body in context variable -->
<set-variable name="clientversion" value="@(((IResponse)context.Variables["clientconfiguresponse"]).Body.As<string>())" />
<!-- Store result in cache -->
<cache-store-value key="@("clientversion-" + context.Variables["clientid"])" value="@((string)context.Variables["clientversion"])" duration="100000" />
</when>
</choose>
<set-backend-service base-url="@(context.Api.ServiceUrl.ToString() + "api/" + (string)context.Variables["clientversion"] + "/")" />
</inbound>
這個優雅的解決方案解決了許多 API 版本控制問題,使 API 消費者能夠透明地控制其客戶端正在訪問的後端版本,而無需更新和重新部署其客戶端。
租用戶隔離
在較大的多租用戶部署中,某些公司會在後端硬體的不同部署上建立個別的租用戶群組。 此結構可將後端出現硬體問題時受影響的客戶數量降到最低。 它還允許分階段推出新的軟件版本。 理想情況下,此後端架構對 API 取用者應該是透明的。 您可以使用類似於透明版本控制的技術來實現這種透明度,使用每個 API 金鑰的配置狀態來操作後端 URL。
您不會傳回每個訂用帳戶金鑰的偏好 API 版本,而是傳回將租用戶與指派硬體群組相關聯的識別碼。 該識別碼可用來建構適當的後端 URL。
總結
自由使用 Azure API 管理 快取來儲存任何類型的資料,可讓您有效率地存取可能影響輸入要求處理方式的組態資料。 它也可用於儲存可增強從後端 API 傳回的回應的資料片段。