Partager via


Mise en cache personnalisée dans Gestion des API Azure

S’APPLIQUE À : Tous les niveaux de Gestion des API

Le service Gestion des API Azure prend en charge la mise en cache de réponse HTTP en utilisant l’URL de ressource comme clé. Vous pouvez modifier la clé à l’aide d’en-têtes de requête qui utilisent les vary-by propriétés. Cette technique est utile pour mettre en cache des réponses HTTP entières (également appelées représentations), mais il est parfois utile de mettre simplement en cache une partie d’une représentation. Les stratégies cache-lookup-value et cache-store-value vous permettent de stocker et de récupérer des éléments de données arbitraires à partir des définitions de stratégie. Cette possibilité ajoute également de la valeur à la stratégie de demande d’envoi , car vous pouvez mettre en cache les réponses des services externes.

Architecture

Le service Gestion des API utilise un cache de données interne partagé par locataire afin que, à mesure que vous effectuez un scale-up vers plusieurs unités, vous avez toujours accès aux mêmes données mises en cache. Toutefois, lors de l’utilisation d’un déploiement multirégion, il existe des caches indépendants dans chacune des régions. Il est important de ne pas traiter le cache comme un magasin de données, où il s’agit de la seule source d’informations. Si vous l’avez fait et que vous avez décidé ultérieurement de tirer parti du déploiement multirégion, les clients avec des utilisateurs qui voyagent peuvent perdre l’accès à ces données mises en cache.

Note

Le cache interne n’est pas disponible dans le niveau Consommation de Gestion des API Azure. Vous pouvez utiliser un cache compatible Redis externe à la place. Un cache externe permet un meilleur contrôle du cache et une plus grande flexibilité pour les instances gestion des API dans tous les niveaux.

Mise en cache de fragments

Il existe certains cas où les réponses retournées contiennent une partie des données coûteuses à déterminer. Pourtant, les données restent fraîches pendant une durée raisonnable. Par exemple, considérez un service créé par une compagnie aérienne qui fournit des informations relatives aux réservations de vols, à l’état du vol, et ainsi de suite. Si l’utilisateur est membre du programme de points d’compagnies aériennes, il aurait également des informations relatives à son état actuel et à son kilométrage cumulé. Ces informations relatives à l’utilisateur peuvent être stockées dans un autre système, mais il peut être souhaitable de l’inclure dans les réponses retournées sur l’état de vol et les réservations. Vous pouvez inclure ces données à l’aide d’un processus appelé mise en cache de fragments. La représentation principale peut être retournée à partir du serveur d’origine à l’aide d’un type de jeton pour indiquer où les informations relatives à l’utilisateur doivent être insérées.

Considérez la réponse JSON suivante à partir d’une API back-end.

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

Et une ressource secondaire chez /userprofile/{userid} ressemble à ceci,

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

Pour déterminer les informations utilisateur appropriées à inclure, Gestion des API doit identifier qui est l’utilisateur final. Ce mécanisme dépend de l’implémentation. L’exemple suivant utilise la revendication Subject d’un jeton JWT.

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

Gestion des API stocke la enduserid valeur dans une variable de contexte pour une utilisation ultérieure. L’étape suivante consiste à déterminer si une demande précédente a déjà récupéré les informations utilisateur et l’a stockée dans le cache. Pour cela, Gestion des API utilise la cache-lookup-value stratégie.

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

Note

Ajoutez une stratégie de limite de débit (ou une stratégie de limite de débit par clé ) après la recherche du cache pour limiter le nombre d’appels et empêcher la surcharge sur le service principal si le cache n’est pas disponible.

S’il n’existe aucune entrée dans le cache qui correspond à la valeur de clé, aucune variable de contexte n’est userprofile créée. La gestion des API vérifie la réussite de la recherche à l’aide de la stratégie de contrôle de flux 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>

Si la userprofile variable de contexte n’existe pas, gestion des API devra effectuer une requête HTTP pour la récupérer.

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

Gestion des API utilise le enduserid pour construire l’URL vers la ressource de profil utilisateur. Une fois que Gestion des API a la réponse, il extrait le texte du corps de la réponse et le stocke dans une variable de contexte.

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

Pour éviter la gestion des API à nouveau lors de la création de cette requête HTTP lorsque le même utilisateur effectue une autre requête, vous pouvez spécifier que le profil utilisateur est stocké dans le cache.

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

Gestion des API stocke la valeur dans le cache avec la même clé que celle initialement utilisée pour tenter de la récupérer. La durée choisie par Gestion des API pour stocker la valeur doit être basée sur la fréquence à laquelle les informations changent et la tolérance des utilisateurs aux informations obsolètes.

Il est important de se rendre compte que la récupération d’informations à partir du cache est toujours une requête réseau hors processus et peut potentiellement ajouter des dizaines de millisecondes à la demande. Les avantages sont liés à la détermination des informations de profil utilisateur qui prennent plus de temps que de récupérer des informations à partir du cache en raison de la nécessité de requêtes de base de données ou d’agrégation d’informations à partir de plusieurs back-ends.

La dernière étape du processus consiste à mettre à jour la réponse retournée avec les informations de profil utilisateur.

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

Vous pouvez choisir d’inclure les guillemets dans le cadre du jeton afin que même lorsque le remplacement ne se produise pas, la réponse est toujours valide JSON.

Une fois que vous avez combiné ces étapes, le résultat final est une stratégie qui ressemble à la suivante.

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

Cette approche de mise en cache est principalement utilisée dans les sites web où le code HTML est composé côté serveur afin qu’il puisse être rendu en tant que page unique. Il peut également être utile dans les API où les clients ne peuvent pas effectuer de mise en cache HTTP côté client ou qu’il est souhaitable de ne pas mettre cette responsabilité sur le client.

Ce même type de mise en cache de fragments peut également être effectué sur les serveurs web principaux à l’aide d’un serveur de mise en cache Redis. Toutefois, l’utilisation du service Gestion des API pour effectuer ce travail est utile lorsque les fragments mis en cache proviennent de différents back-ends que les réponses principales.

Gestion de versions transparente

Il est courant que plusieurs versions d’implémentation différentes d’une API soient prises en charge à tout moment. Par exemple, pour prendre en charge différents environnements (développement, test, production, etc.) ou pour prendre en charge les versions antérieures de l’API afin de donner du temps aux consommateurs d’API de migrer vers des versions plus récentes.

Une approche pour gérer plusieurs versions, au lieu d’exiger que les développeurs côté client modifient les URL de /v1/customers vers /v2/customers, consiste à stocker dans les données de profil du consommateur la version de l’API qu'il souhaite utiliser et à appeler l'URL du serveur principal appropriée. Pour déterminer l’URL principale appropriée à appeler pour un client particulier, il est nécessaire d’interroger certaines données de configuration. Lorsque vous mettez en cache ces données de configuration, API Management peut réduire la pénalité de performances de cette recherche.

La première étape consiste à déterminer l’identificateur utilisé pour configurer la version souhaitée. Dans cet exemple, nous allons associer la version à la clé d’abonnement du produit.

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

Gestion des API effectue ensuite une recherche de cache pour voir s’il a déjà récupéré la version du client souhaitée.

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

Note

Ajoutez une stratégie de limite de débit (ou une stratégie de limite de débit par clé ) après la recherche du cache pour limiter le nombre d’appels et empêcher la surcharge sur le service principal si le cache n’est pas disponible.

Ensuite, Gestion des API vérifie s’il ne le trouve pas dans le cache.

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

Si la gestion des API ne le trouve pas, elle le récupère.

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

Extrayez le texte du corps de réponse à partir de la réponse.

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

Stockez-le dans le cache pour une utilisation ultérieure.

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

Enfin, mettez à jour l’URL principale pour sélectionner la version du service souhaité par le client.

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

La stratégie complète est la suivante :

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

Cette solution élégante répond à de nombreux problèmes de contrôle de version d’API, en permettant aux consommateurs d’API de contrôler de manière transparente la version principale à laquelle leurs clients accèdent sans avoir à mettre à jour et redéployer leurs clients.

Isolation des locataires

Dans les déploiements multilocataires plus volumineux, certaines entreprises créent des groupes distincts de locataires sur des déploiements distincts de matériel principal. Cette structure réduit le nombre de clients affectés lorsqu’il existe un problème matériel sur le serveur principal. Il permet également de déployer de nouvelles versions logicielles en phases. Dans l’idéal, cette architecture back-end doit être transparente pour les consommateurs d’API. Vous pouvez obtenir cette transparence à l’aide d’une technique similaire au contrôle de version transparent, en manipulant l’URL principale à l’aide de l’état de configuration par clé API.

Au lieu de retourner une version préférée de l’API pour chaque clé d’abonnement, vous renvoyez un identificateur qui concerne un locataire au groupe matériel affecté. Cet identificateur peut être utilisé pour construire l’URL principale appropriée.

Résumé

La liberté d’utiliser le cache de gestion des API Azure pour stocker n’importe quel type de données permet un accès efficace aux données de configuration qui peuvent affecter la façon dont une demande entrante est traitée. Il peut également être utilisé pour stocker des fragments de données qui peuvent augmenter les réponses, retournées par une API back-end.