Comparteix via


Caché central de metadatos para fragmentos de directiva

SE APLICA A: Todos los niveles de API Management

Cuando varios fragmentos de directiva necesitan acceso a metadatos compartidos, como datos de configuración comunes, use un enfoque de almacenamiento en caché entre solicitudes para optimizar el rendimiento. En lugar de analizar los metadatos repetidamente en cada fragmento, un enfoque de análisis una vez y caché en todas partes mejora considerablemente el rendimiento al tiempo que garantiza la coherencia de los datos. Con este enfoque, los metadatos se analizan una vez en la primera solicitud cuando la caché está vacía y, a continuación, se recuperan de la memoria caché para todas las solicitudes posteriores hasta que expire la memoria caché o cambie la versión de la memoria caché.

Este enfoque requiere dos fragmentos: uno para almacenar metadatos compartidos y otro para analizar y almacenar en caché los metadatos.

1. Fragmento de metadatos

El fragmento de metadatos actúa como el único origen de la verdad para los metadatos compartidos a los que acceden otros fragmentos de la canalización:

  • Almacenamiento JSON centralizado: almacena todos los metadatos como JSON.
  • Configuración de caché: incluye la configuración de caché con control de versiones y duración (Período de vida o TTL).

2. Análisis y almacenamiento en caché de fragmentos

El análisis y el almacenamiento en caché del fragmento implementan los comportamientos siguientes:

  • Operación de análisis único: usa JObject.Parse() para analizar el JSON almacenado en el fragmento de metadatos una vez al principio de cada solicitud de canalización si la memoria caché está vacía.
  • Almacenamiento en caché entre solicitudes: almacena y recupera secciones de metadatos analizados como un JObject mediante las directivas integradas cache-store-value y cache-lookup-value en varias solicitudes.
  • Acceso primero a la caché: las solicitudes posteriores recuperan un objeto analizado JObject directamente desde la memoria caché, lo que proporciona acceso inmediato a todos los fragmentos sin volver a analizar.
  • Cache invalidation: la caché se actualiza cuando cambia la versión de metadatos o expira el tiempo de vida de la caché (TTL).

Detalles de la implementación

Para implementar este patrón, inserte ambos fragmentos en una definición de directiva de API o producto al principio de la fase de entrada. El fragmento de metadatos debe insertarse primero, seguido del análisis y el almacenamiento en caché del fragmento. Por ejemplo:

<policies>
    <inbound>
        <base />
        <include-fragment fragment-id="metadata-fragment" />
        <include-fragment fragment-id="parse-cache-fragment" />
    </inbound>
</policies>

Ejemplo de fragmento de metadatos

El metadata-fragment.xml fragmento almacena metadatos JSON compartidos en una variable de contexto denominada 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>

Ejemplo de fragmento de análisis y almacenamiento en caché

El parse-cache-fragment.xml fragmento analiza el JSON almacenado en la metadata-config variable de contexto una vez y proporciona acceso al objeto resultante JObject. La metadata-config variable ya debe establecerse mediante 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>

Uso de metadatos en otros fragmentos

Otros fragmentos ahora pueden acceder directamente a secciones de metadatos analizados. Por ejemplo:

<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>

Configuración y invalidación de caché

El parse-cache-fragment.xml fragmento usa la configuración de caché almacenada en el metadata-fragment.xml fragmento para determinar el comportamiento y la invalidación del almacenamiento en caché. Por ejemplo, la configuración se puede cambiar de la siguiente manera:

<!-- 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'
  }
}

Funcionamiento de la invalidación de caché: El parse-cache-fragment.xml fragmento construye claves de caché mediante el config-version valor (por ejemplo, metadata-config-v1.0.1). Cuando se cambia la versión a 1.0.2, se crea una nueva clave de caché (metadata-config-v1.0.2). Dado que no existe ningún dato almacenado en caché para la nueva clave, el fragmento analiza json de metadatos nuevos.

Para forzar el refresco de caché: Actualice el fragmento config-version en metadata-fragment.xml. Dado que la configuración de caché se analiza en cada solicitud antes de que se produzca la búsqueda de caché, los cambios en la configuración de la caché surten efecto inmediatamente.

Pruebas y depuración

Seguimiento de resultados de caché

El parse-cache-fragment.xml fragmento establece una config-cache-result variable. Esta variable es útil para el registro y en los encabezados de respuesta para la depuración.

<!-- 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>

Omisión de caché

Para deshabilitar el almacenamiento en caché, use el encabezado de omisión de caché:

curl -H "X-Config-Cache-Bypass: true" https://your-gateway.com/api

El parse-cache-fragment.xml fragmento comprueba el encabezado de bypass después de analizar la configuración de caché.

<!-- 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";
}" />

A continuación, la comprobación de omisión se usa en la lógica de decisión de almacenamiento en caché:

<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>

procedimientos recomendados

Control de errores de análisis de JSON con el registro de errores y los valores predeterminados

Implemente el control de errores para las operaciones de análisis de JSON para evitar fallos de fragmentos y proporcionar un comportamiento alternativo. Envuelve JObject.Parse() las operaciones en bloques de try-catch con valores predeterminados significativos.

<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>