适用于:所有 API 管理层级
当多个策略片段需要访问共享元数据(如通用配置数据)时,请使用跨请求缓存方法来优化性能。 与在每个片段中重复分析元数据相比,分析一次,随处缓存方法可显著提高性能,同时确保数据一致性。 使用此方法,当缓存为空时,在第一个请求上 分析一次 元数据,然后在缓存过期或缓存版本更改之前 从缓存中检索 所有后续请求。
推荐的方法
此方法需要两个片段:一个用于存储共享元数据,另一个用于分析和缓存元数据。
1. 元数据片段
元数据片段充当管道中其他片段访问的共享元数据的单一事实来源:
- 集中式 JSON 存储:将所有元数据存储为 JSON。
- 缓存设置:包括具有版本控制和持续时间的缓存设置(生存时间或 TTL)。
2. 分析和缓存片段
分析和缓存片段实现以下行为:
-
单次解析操作:如果缓存为空,将在每个管道请求开始时使用
JObject.Parse()对存储在元数据片段中的 JSON 进行一次解析。 -
跨请求缓存:在多个请求中,使用 内置的
JObject和 cache-lookup-value 策略,作为 存储和检索解析的元数据部分。 -
缓存优先访问:后续请求直接从缓存检索分析
JObject,从而提供对所有片段的即时访问,而无需重新分析。 - 缓存失效:当元数据版本更改或缓存持续时间 (TTL) 过期时,缓存会刷新。
实施细节
若要实现此模式,请将这两个片段插入到入站阶段开始时的产品或 API 策略定义中。 必须先插入元数据片段,然后插入分析和缓存片段。 例如:
<policies>
<inbound>
<base />
<include-fragment fragment-id="metadata-fragment" />
<include-fragment fragment-id="parse-cache-fragment" />
</inbound>
</policies>
元数据片段示例
片段metadata-fragment.xml将共享的 JSON 元数据存储在名为上下文变量的metadata-config中:
<!-- Single source of truth for all shared metadata -->
<fragment fragment-id="metadata-fragment">
<set-variable name="metadata-config" value="@{return @"{
'cache-settings': {
'config-version': '1.0',
'ttl-seconds': 3600,
'feature-flags': {
'enable-cross-request-cache': true,
'cache-bypass-header': 'X-Config-Cache-Bypass'
}
},
'logging': {
'level': 'INFO',
'enabled': true
},
'rate-limits': {
'premium': { 'requests-per-minute': 1000 },
'standard': { 'requests-per-minute': 100 },
'basic': { 'requests-per-minute': 20 }
}
}";}" />
</fragment>
分析和缓存片段示例
片段 parse-cache-fragment.xml 分析存储在上下文变量中的 metadata-config JSON 一次,并提供对生成的 JObject访问。 变量 metadata-config 必须已由以下项 metadata-fragment.xml设置:
<fragment fragment-id="parse-cache-fragment">
<!-- Extract cache settings from metadata-config to determine cache version and TTL -->
<set-variable name="cache-config-temp" value="@{
try {
var configStr = context.Variables.GetValueOrDefault<string>("metadata-config", "{}");
if (string.IsNullOrEmpty(configStr) || configStr == "{}") {
return "{\"version\":\"1.0\",\"enabled\":true,\"ttl\":3600}";
}
var tempConfig = JObject.Parse(configStr);
var cacheSettings = tempConfig["cache-settings"] as JObject;
var result = new JObject();
result["version"] = cacheSettings?["config-version"]?.ToString() ?? "1.0";
result["enabled"] = cacheSettings?["feature-flags"]?["enable-cross-request-cache"]?.Value<bool>() ?? true;
result["ttl"] = cacheSettings?["ttl-seconds"]?.Value<int>() ?? 3600;
return result.ToString(Newtonsoft.Json.Formatting.None);
} catch {
return "{\"version\":\"1.0\",\"enabled\":true,\"ttl\":3600}";
}
}" />
<!-- Parse cache configuration -->
<set-variable name="cache-settings-parsed" value="@{
return JObject.Parse(context.Variables.GetValueOrDefault<string>("cache-config-temp", "{}"));
}" />
<!-- Build cache key with version from cache settings -->
<set-variable name="cache-key" value="@{
var settings = context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed");
var version = settings?["version"]?.ToString() ?? "1.0";
return "metadata-config-parsed-v" + version;
}" />
<!-- Try to get from APIM cache -->
<cache-lookup-value key="@(context.Variables.GetValueOrDefault<string>("cache-key"))" variable-name="cached-config" />
<choose>
<when condition="@(context.Variables.ContainsKey("cached-config"))">
<!-- Cache found - Use cached configuration -->
<set-variable name="config-cache-result" value="@(true)" />
<!-- Restore cached config-parsed -->
<set-variable name="config-parsed" value="@(context.Variables.GetValueOrDefault<JObject>("cached-config"))" />
<!-- Extract sections from cached metadata JObject -->
<set-variable name="config-logging" value="@{
var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
return config["logging"] as JObject ?? new JObject();
}" />
<set-variable name="config-rate-limits" value="@{
var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
return config["rate-limits"] as JObject ?? new JObject();
}" />
</when>
<otherwise>
<!-- Cache miss - Parse and store in cache -->
<set-variable name="config-cache-result" value="@(false)" />
<!-- Parse metadata-config JSON -->
<set-variable name="config-parsed" value="@{
var configStr = context.Variables.GetValueOrDefault<string>("metadata-config", "{}");
return JObject.Parse(configStr);
}" />
<!-- Extract commonly used sections for direct access -->
<set-variable name="config-logging" value="@{
var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
return config["logging"] as JObject ?? new JObject();
}" />
<set-variable name="config-rate-limits" value="@{
var config = context.Variables.GetValueOrDefault<JObject>("config-parsed");
return config["rate-limits"] as JObject ?? new JObject();
}" />
<!-- Store parsed metadata JObject in cache -->
<cache-store-value key="@(context.Variables.GetValueOrDefault<string>("cache-key"))"
value="@(context.Variables.GetValueOrDefault<JObject>("config-parsed"))"
duration="@(context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed")?["ttl"]?.Value<int>() ?? 3600)" />
</otherwise>
</choose>
</fragment>
在其他片段中使用元数据
其他片段现在可以直接访问分析的元数据部分。 例如:
<fragment fragment-id="request-logging-fragment">
<!-- Access logging metadata JObject without reparsing -->
<set-variable name="config-logging" value="@{
return context.Variables.GetValueOrDefault<JObject>("config-logging", new JObject());
}" />
</fragment>
缓存设置和失效
该 parse-cache-fragment.xml 片段使用片段中存储的 metadata-fragment.xml 缓存设置来确定缓存行为和失效。 例如,可按如下所示更改设置:
<!-- Example: Updated cache settings in the metadata fragment -->
'cache-settings': {
'config-version': '1.0.1', <!-- Change version to invalidate cache -->
'ttl-seconds': 7200, <!-- Increase TTL to 2 hours -->
'feature-flags': {
'enable-cross-request-cache': true,
'cache-bypass-header': 'X-Config-Cache-Bypass'
}
}
缓存失效的工作原理: 片段 parse-cache-fragment.xml 使用 config-version 值(例如, metadata-config-v1.0.1)构造缓存键。 当版本更改为 1.0.2时,将创建一个新的缓存密钥(metadata-config-v1.0.2)。 由于新键不存在缓存的数据,片段将分析新的元数据 JSON。
强制刷新缓存:在config-version片段中更新metadata-fragment.xml。 由于缓存设置在缓存查找发生之前对每个请求进行分析,因此对缓存配置的更改会立即生效。
测试和调试
缓存结果跟踪
片段 parse-cache-fragment.xml 设置变量 config-cache-result 。 此变量可用于日志记录并用于响应标头中的调试。
<!-- Add cache status to response headers for debugging -->
<set-header name="X-Config-Cache-Result" exists-action="override">
<value>@(context.Variables.GetValueOrDefault<bool>("config-cache-result", false).ToString())</value>
</set-header>
缓存绕过
若要禁用缓存,请使用缓存绕过标头:
curl -H "X-Config-Cache-Bypass: true" https://your-gateway.com/api
片段 parse-cache-fragment.xml 在分析缓存设置后检查绕过标头:
<!-- Check if cache bypass is requested -->
<set-variable name="cache-bypass-requested" value="@{
var settings = context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed");
var bypassHeader = settings?["bypass-header"]?.ToString() ?? "X-Config-Cache-Bypass";
return context.Request.Headers.GetValueOrDefault(bypassHeader, "").ToLower() == "true";
}" />
然后,在缓存决策逻辑中使用绕过检查:
<when condition="@{
var settings = context.Variables.GetValueOrDefault<JObject>("cache-settings-parsed");
var enabled = settings?["enabled"]?.Value<bool>() ?? false;
var bypass = context.Variables.GetValueOrDefault<bool>("cache-bypass-requested", false);
return enabled && !bypass;
}">
<!-- Cross-request caching is enabled and not bypassed -->
</when>
最佳做法
使用错误日志记录和默认值处理 JSON 分析失败
为 JSON 解析实现错误处理,以预防解析过程中的故障,并提供回退机制。 在 try-catch 块中使用有意义的默认值来包装 JObject.Parse() 操作。
<set-variable name="config-parsed" value="@{
try {
var configJson = context.Variables.GetValueOrDefault<string>("metadata-config", "{}");
return JObject.Parse(configJson);
} catch (Exception ex) {
// Return default configuration on parse failure
return JObject.Parse(@"{
'logging': { 'level': 'ERROR', 'enabled': false },
'rate-limits': { 'default': { 'requests-per-minute': 10 } }
}");
}
}" />
<!-- Log parse error using trace policy -->
<choose>
<when condition="@(context.Variables.ContainsKey("parse-error"))">
<trace source="config-parse" severity="error">
<message>@("JSON parse failed: " + context.Variables.GetValueOrDefault<string>("parse-error"))</message>
</trace>
</when>
</choose>
相关内容
- 用于使用策略片段生成高级执行管道的体系结构 - 用于设计模块化、可缩放的策略片段体系结构的基础模式,并明确分离关注点。
- 策略片段的变量管理 - 有关上下文变量处理、安全访问模式和片段间通信的综合指南。
- 策略注入和与片段的协调 - 片段注入模式以及产品和 API 策略之间的协调。
- Azure API 管理中的自定义缓存 - 了解如何按键缓存项,以及如何使用请求标头修改密钥。