Delen via


Aangepaste caching in Azure API Management

Van toepassing op: Alle API Management-lagen

De Azure API Management-service biedt ingebouwde ondersteuning voor http-antwoordcaching met behulp van de resource-URL als sleutel. U kunt de sleutel wijzigen met behulp van aanvraagheaders die gebruikmaken van de vary-by eigenschappen. Deze techniek is handig voor het opslaan van volledige HTTP-antwoorden (ook wel weergaven genoemd), maar soms is het handig om een deel van een weergave in de cache op te cachen. Het cache-zoekwaarde - en cache-store-waardebeleid biedt u de mogelijkheid om willekeurige stukjes gegevens op te slaan en op te halen uit beleidsdefinities. Deze mogelijkheid voegt ook waarde toe aan het beleid voor verzenden-aanvragen , omdat u reacties van externe services in de cache kunt opslaan.

Architecture

API Management-service maakt gebruik van een gedeelde interne gegevenscache per tenant, zodat u, wanneer u omhoog schaalt naar meerdere eenheden, nog steeds toegang krijgt tot dezelfde gegevens in de cache. Bij het werken met een implementatie met meerdere regio's zijn er echter onafhankelijke caches binnen elk van de regio's. Het is belangrijk om de cache niet te behandelen als een gegevensarchief, waarbij het de enige bron van bepaalde informatie is. Als u dat hebt gedaan en later hebt besloten om te profiteren van de implementatie met meerdere regio's, kunnen klanten met gebruikers die reizen de toegang tot die gegevens in de cache verliezen.

Opmerking

De interne cache is niet beschikbaar in de verbruikslaag van Azure API Management. U kunt in plaats daarvan een externe Redis-compatibele cache gebruiken . Een externe cache biedt meer cachebeheer en flexibiliteit voor API Management-exemplaren in alle lagen.

Fragmentcaching

Er zijn bepaalde gevallen waarin de antwoorden die worden geretourneerd, een deel van de gegevens bevatten dat duur is om te bepalen. Toch blijven de gegevens gedurende een redelijke tijd actueel. Denk bijvoorbeeld aan een service die is gebouwd door een luchtvaartmaatschappij die informatie biedt met betrekking tot vluchtreserveringen, vluchtstatus, enzovoort. Als de gebruiker lid is van het programma voor luchtvaartmaatschappijenpunten, zou hij ook informatie hebben met betrekking tot hun huidige status en geaccumuleerde kilometers. Deze gebruikersgerelateerde informatie kan worden opgeslagen in een ander systeem, maar het kan wenselijk zijn om deze op te nemen in antwoorden die worden geretourneerd over de vluchtstatus en reserveringen. U kunt deze gegevens opnemen met behulp van een proces met de naam fragmentcaching. De primaire weergave kan worden geretourneerd van de oorspronkelijke server met behulp van een bepaald type token om aan te geven waar de gebruikersgerelateerde informatie moet worden ingevoegd.

Bekijk het volgende JSON-antwoord van een back-end-API.

{
  "airline" : "Air Canada",
  "flightno" : "871",
  "status" : "ontime",
  "gate" : "B40",
  "terminal" : "2A",
  "userprofile" : "$userprofile$"
}  

En een secundaire resource die /userprofile/{userid} er als volgt uitziet,

{ "username" : "Bob Smith", "Status" : "Gold" }

Om de juiste gebruikersgegevens te bepalen die moeten worden opgenomen, moet API Management bepalen wie de eindgebruiker is. Dit mechanisme is afhankelijk van de implementatie. In het volgende voorbeeld wordt de Subject claim van een JWT token gebruikt.

<set-variable
  name="enduserid"
  value="@(context.Request.Headers.GetValueOrDefault("Authorization","").Split(' ')[1].AsJwt()?.Subject)" />

API Management slaat de enduserid waarde op in een contextvariabele voor later gebruik. De volgende stap is om te bepalen of een vorige aanvraag de gebruikersgegevens al heeft opgehaald en opgeslagen in de cache. Hiervoor maakt API Management gebruik van het cache-lookup-value beleid.

<cache-lookup-value
key="@("userprofile-" + context.Variables["enduserid"])"
variable-name="userprofile" />
<rate-limit calls="10" renewal-period="60" />

Opmerking

Het beleid voor frequentielimiet dat na het cache-opzoekproces is toegevoegd, helpt het aantal aanroepen te beperken om te voorkomen dat de backendservice overbelast raakt ingeval de cache niet beschikbaar is.

Als er geen vermelding in de cache is die overeenkomt met de sleutelwaarde, wordt er geen userprofile contextvariabele gemaakt. API Management controleert het succes van de zoekactie met behulp van het choose controlestroombeleid.

<choose>
    <when condition="@(!context.Variables.ContainsKey("userprofile"))">
        <!-- If the userprofile context variable doesn’t exist, make an HTTP request to retrieve it.  -->
    </when>
</choose>

Als de userprofile contextvariabele niet bestaat, moet API Management een HTTP-aanvraag indienen om deze op te halen.

<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 Management gebruikt de enduserid om de URL naar de gebruikersprofielresource samen te stellen. Zodra API Management het antwoord heeft, wordt de hoofdtekst uit het antwoord gehaald en weer opgeslagen in een contextvariabele.

<set-variable
    name="userprofile"
    value="@(((IResponse)context.Variables["userprofileresponse"]).Body.As<string>())" />

Als u wilt voorkomen dat API Management deze HTTP-aanvraag opnieuw indient wanneer dezelfde gebruiker een andere aanvraag doet, kunt u opgeven dat het gebruikersprofiel wordt opgeslagen in de cache.

<cache-store-value
    key="@("userprofile-" + context.Variables["enduserid"])"
    value="@((string)context.Variables["userprofile"])" duration="100000" />

API Management slaat de waarde op in de cache met dezelfde sleutel waarmee API Management oorspronkelijk heeft geprobeerd deze op te halen. De duur die API Management kiest om de waarde op te slaan, moet zijn gebaseerd op hoe vaak de informatie verandert en hoe tolerant gebruikers verouderde informatie hebben.

Het is belangrijk te beseffen dat het ophalen van gegevens uit de cache nog steeds een niet-verwerkte netwerkaanvraag is en mogelijk tientallen milliseconden aan de aanvraag kan toevoegen. De voordelen komen naar voren wanneer het bepalen van de gebruikersprofielinformatie langer duurt dan het ophalen van informatie uit de cache, vanwege de noodzaak voor databasequery's of het aggregeren van gegevens uit meerdere back-ends.

De laatste stap in het proces is het bijwerken van het geretourneerde antwoord met de gebruikersprofielgegevens.

<!-- Update response body with user profile-->
<find-and-replace
    from='"$userprofile$"'
    to="@((string)context.Variables["userprofile"])" />

U kunt ervoor kiezen om de aanhalingstekens op te nemen als onderdeel van het token, zodat zelfs wanneer de vervanging niet plaatsvindt, het antwoord nog steeds geldige JSON is.

Zodra u deze stappen combineert, is het eindresultaat een beleid dat lijkt op de volgende.

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

Deze cachingbenadering wordt voornamelijk gebruikt in websites waar HTML is samengesteld aan de serverzijde, zodat deze kan worden weergegeven als één pagina. Het kan ook handig zijn in API's waarbij clients http-caching aan clientzijde niet kunnen uitvoeren of het wenselijk is om die verantwoordelijkheid niet op de client te plaatsen.

Dit soort fragmentcaching kan ook worden uitgevoerd op de back-endwebservers met behulp van een Redis-cacheserver. Het gebruik van de API Management-service om dit werk uit te voeren is echter handig wanneer de fragmenten in de cache afkomstig zijn van verschillende back-ends dan de primaire antwoorden.

Transparante versiebeheer

Het is gebruikelijk dat meerdere verschillende implementatieversies van een API op elk gewenst moment worden ondersteund. Bijvoorbeeld om verschillende omgevingen te ondersteunen (dev, test, productie, enzovoort) of om oudere versies van de API te ondersteunen om gebruikers van de API tijd te geven om te migreren naar nieuwere versies.

Een benadering voor het verwerken van meerdere versies, in plaats van dat clientontwikkelaars de URL's moeten wijzigen van /v1/customers in /v2/customers, is het opslaan in de profielgegevens van de consument welke versie van de API ze momenteel willen gebruiken en de juiste back-end-URL aanroepen. Als u de juiste back-end-URL wilt bepalen die moet worden aangeroepen voor een bepaalde client, moet u een query uitvoeren op bepaalde configuratiegegevens. Wanneer u deze configuratiegegevens in de cache opgeslagen, kan API Management de prestatiestraf voor het uitvoeren van deze zoekopdracht minimaliseren.

De eerste stap is het bepalen van de id die wordt gebruikt om de gewenste versie te configureren. In dit voorbeeld koppelen we de versie aan de productabonnementscode.

<set-variable name="clientid" value="@(context.Subscription.Key)" />

API Management voert vervolgens een cachezoekactie uit om te zien of deze al de gewenste clientversie heeft opgehaald.

<cache-lookup-value
key="@("clientversion-" + context.Variables["clientid"])"
variable-name="clientversion" />
<rate-limit calls="10" renewal-period="60" />

Opmerking

Het beleid voor frequentielimiet dat na het cache-opzoekproces is toegevoegd, helpt het aantal aanroepen te beperken om te voorkomen dat de backendservice overbelast raakt ingeval de cache niet beschikbaar is.

Vervolgens controleert API Management of deze niet in de cache is gevonden.

<choose>
    <when condition="@(!context.Variables.ContainsKey("clientversion"))">

Als API Management het niet heeft gevonden, haalt API Management het op.

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

Haal de hoofdtekst van de respons uit de respons.

<set-variable
      name="clientversion"
      value="@(((IResponse)context.Variables["clientconfiguresponse"]).Body.As<string>())" />

Sla deze weer op in de cache voor toekomstig gebruik.

<cache-store-value
      key="@("clientversion-" + context.Variables["clientid"])"
      value="@((string)context.Variables["clientversion"])"
      duration="100000" />

En ten slotte werkt u de back-end-URL bij om de versie van de service te selecteren die door de client is gewenst.

<set-backend-service
      base-url="@(context.Api.ServiceUrl.ToString() + "api/" + (string)context.Variables["clientversion"] + "/")" />

Het volledige beleid is als volgt:

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

Deze elegante oplossing lost veel problemen met API-versies op door API-gebruikers in staat te stellen transparant te bepalen welke back-endversie hun clients openen zonder dat ze hun clients hoeven bij te werken en opnieuw te implementeren.

Isolatie van tenants

In grotere implementaties met meerdere tenants maken sommige bedrijven afzonderlijke groepen tenants op afzonderlijke implementaties van back-endhardware. Deze structuur minimaliseert het aantal klanten dat wordt beïnvloed wanneer er een hardwareprobleem is op de back-end. Ook kunnen nieuwe softwareversies in fasen worden geïmplementeerd. In het ideale geval moet deze back-endarchitectuur transparant zijn voor API-consumenten. U kunt deze transparantie bereiken met behulp van een techniek die vergelijkbaar is met transparante versiebeheer, waarbij u de back-end-URL bewerkt met behulp van de configuratiestatus per API-sleutel.

In plaats van een voorkeursversie van de API te retourneren voor elke abonnementssleutel, retourneert u een id die een tenant aan de toegewezen hardwaregroep relateert. Deze id kan worden gebruikt om de juiste back-end-URL te maken.

Samenvatting

De vrijheid om de Azure API Management-cache te gebruiken voor het opslaan van gegevens, maakt efficiënte toegang tot configuratiegegevens mogelijk die van invloed kunnen zijn op de manier waarop een binnenkomende aanvraag wordt verwerkt. Het kan ook worden gebruikt voor het opslaan van gegevensfragmenten die antwoorden kunnen uitbreiden, geretourneerd door een back-end-API.