Teilen über


Zentraler Metadatencache für Richtlinienfragmente

GILT FÜR: Alle API Management-Ebenen

Wenn mehrere Richtlinienfragmente Zugriff auf freigegebene Metadaten wie allgemeine Konfigurationsdaten benötigen, verwenden Sie einen anforderungsübergreifenden Zwischenspeicherungsansatz, um die Leistung zu optimieren. Anstatt Metadaten wiederholt in jedem Fragment zu analysieren, verbessert ein Ansatz des einmaligen Parsens und überall Cachens die Leistung erheblich, während gleichzeitig die Datenkonsistenz sichergestellt wird. Bei diesem Ansatz werden Metadaten einmal in der ersten Anforderung analysiert , wenn der Cache leer ist, und dann aus dem Cache für alle nachfolgenden Anforderungen abgerufen, bis der Cache abläuft oder die Cacheversion geändert wird.

Dieser Ansatz erfordert zwei Fragmente: eines zum Speichern freigegebener Metadaten und eines anderen zum Analysieren und Zwischenspeichern der Metadaten.

1. Metadatenfragment

Das Metadatenfragment dient als einzige Wahrheitsquelle für freigegebene Metadaten, auf die von anderen Fragmenten in der Pipeline zugegriffen wird:

  • Zentralisierter JSON-Speicher: Speichert alle Metadaten als JSON.
  • Cacheeinstellungen: Umfasst Cacheeinstellungen mit Versionsverwaltung und Dauer (Zeit für Live oder TTL).

2. Fragment für Analyse und Zwischenspeicherung

Das Analyse- und Zwischenspeicherungsfragment implementiert das folgende Verhalten:

  • Einzelner Analysevorgang: Verwendet JObject.Parse() , um den im Metadatenfragment gespeicherten JSON-Code einmal am Anfang jeder Pipelineanforderung zu analysieren, wenn der Cache leer ist.
  • Anforderungsübergreifendes Zwischenspeichern: Speichert und ruft analysierte Metadatenabschnitte mithilfe JObject der integrierten Cachespeicherwert- und Cache-Nachschlagewertrichtlinien für mehrere Anforderungen ab.
  • Cache-first-Zugriff: Nachfolgende Anforderungen rufen ein analysiertes JObject direkt aus dem Cache ab, wodurch sofortiger Zugriff auf alle Fragmente ohne erneute Analyse ermöglicht wird.
  • Cache-Invalidierung: Der Cache wird aktualisiert, wenn sich die Metadatenversion ändert oder die Cachedauer (TTL) abläuft.

Details zur Implementierung

Um dieses Muster zu implementieren, fügen Sie beide Fragmente am Anfang der eingehenden Phase in eine Produkt- oder API-Richtliniendefinition ein. Das Metadatenfragment muss zuerst eingefügt werden, gefolgt von dem Analyse- und Zwischenspeicherungsfragment. Beispiel:

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

Beispiel für Metadatenfragment

Das metadata-fragment.xml Fragment speichert freigegebene JSON-Metadaten in einer Kontextvariablen mit dem Namen 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>

Beispiel zum Analysieren und Zwischenspeichern von Fragmenten

Das parse-cache-fragment.xml Fragment analysiert den JSON-Code, der in der metadata-config Kontextvariable gespeichert ist, einmal und bietet Zugriff auf die resultierende JObject. Die metadata-config Variable muss bereits festgelegt werden durch 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>

Verwenden von Metadaten in anderen Fragmenten

Andere Fragmente können jetzt direkt auf analysierte Metadatenabschnitte zugreifen. Beispiel:

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

Cacheeinstellungen und Ungültigheit

Das parse-cache-fragment.xml Fragment verwendet die im metadata-fragment.xml Fragment gespeicherten Cacheeinstellungen, um das Zwischenspeicherungsverhalten und die Ungültigheit zu bestimmen. Beispielsweise können die Einstellungen wie folgt geändert werden:

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

Funktionsweise der Cache-Ungültigheit: Das parse-cache-fragment.xml Fragment erstellt Cacheschlüssel mithilfe des config-version Werts (z. B metadata-config-v1.0.1. ). Wenn die Version auf 1.0.2 geändert wird, wird ein neuer Cacheschlüssel erstellt (metadata-config-v1.0.2). Da für den neuen Schlüssel keine zwischengespeicherten Daten vorhanden sind, analysiert das Fragment neue Metadaten-JSON.

Cacheaktualisierung erzwingen: Aktualisieren Sie das config-version im metadata-fragment.xml-Fragment. Da die Cacheeinstellungen für jede Anforderung analysiert werden, bevor die Cachesuche auftritt, werden Änderungen an der Cachekonfiguration sofort wirksam.

Testen und Debuggen

Cacheergebnisnachverfolgung

Das parse-cache-fragment.xml Fragment legt eine config-cache-result Variable fest. Diese Variable ist nützlich für die Protokollierung und in Antwortheadern zum Debuggen:

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

Cache-Umgehung

Verwenden Sie den Cacheumgehungsheader, um die Zwischenspeicherung zu deaktivieren:

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

Das parse-cache-fragment.xml fragment überprüft den Umgehungsheader nach der Analyse von Cacheeinstellungen.

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

Die Umgehungsprüfung wird dann in der Entscheidungslogik für die Zwischenspeicherung verwendet:

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

Bewährte Methoden

Behandeln von JSON-Analysefehlern mit Fehlerprotokollierung und Standardwerten

Implementieren Sie die Fehlerbehandlung für JSON-Analysevorgänge, um Fragmentfehler zu verhindern und Fallbackverhalten bereitzustellen. Verpacken Sie JObject.Parse()-Operationen in Try-Catch-Blöcke mit aussagekräftigen Standardwerten:

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