Udostępnij za pomocą


Niestandardowe buforowanie w usłudze Azure API Management

DOTYCZY: Wszystkich poziomów zarządzania API

Usługa Azure API Management ma wbudowaną obsługę buforowania odpowiedzi HTTP przy użyciu adresu URL zasobu jako klucza. Aby zmodyfikować klucz, można użyć nagłówków żądań, które korzystają z właściwości vary-by. Ta technika jest przydatna w przypadku buforowania całych odpowiedzi HTTP (nazywanych również reprezentacjami), ale czasami przydaje się tylko buforowanie części reprezentacji. Zasady cache-lookup-value i cache-store-value umożliwiają przechowywanie i pobieranie dowolnych fragmentów danych z definicji zasad. Ta możliwość dodaje również wartość do zasad wysyłania żądań , ponieważ można buforować odpowiedzi z usług zewnętrznych.

Architecture

Usługa API Management używa pamięci podręcznej współdzielonej na dzierżawę, dzięki czemu przy skalowaniu do wielu jednostek nadal zachowujesz dostęp do tych samych danych w pamięci podręcznej. Jednak podczas pracy z wieloregionalnym wdrożeniem istnieją niezależne bufory w każdym z regionów. Ważne jest, aby nie traktować pamięci podręcznej jako magazynu danych, gdzie jest jedynym źródłem niektórych informacji. Jeśli tak, a później zdecydujesz się skorzystać z wdrożenia w wielu regionach, klienci z użytkownikami, którzy podróżują, mogą utracić dostęp do tych buforowanych danych.

Uwaga / Notatka

Wewnętrzna pamięć podręczna nie jest dostępna w warstwie Zużycie usługi Azure API Management. Zamiast tego można użyć zewnętrznej pamięci podręcznej zgodnej z usługą Redis . Zewnętrzna pamięć podręczna umożliwia większą kontrolę i elastyczność obsługi wystąpień usługi API Management we wszystkich warstwach.

Buforowanie fragmentów

Istnieją pewne przypadki, w których zwracane odpowiedzi zawierają część danych, która jest kosztowna do określenia. Jednak dane pozostają świeże przez rozsądny czas. Rozważmy na przykład usługę utworzoną przez linię lotniczą, która dostarcza informacje dotyczące rezerwacji lotów, stanu lotu itd. Jeśli użytkownik jest członkiem programu punktów lotniczych, będzie miał również informacje dotyczące ich bieżącego statusu i skumulowanego przebiegu. Te informacje związane z użytkownikiem mogą być przechowywane w innym systemie, ale może być pożądane, aby uwzględnić je w odpowiedziach zwróconych na temat stanu lotu i rezerwacji. Te dane można uwzględnić przy użyciu procesu nazywanego buforowaniem fragmentów. Podstawowa reprezentacja może zostać zwrócona z serwera pochodzenia przy użyciu jakiegoś tokenu, aby wskazać, gdzie mają zostać wstawione informacje dotyczące użytkownika.

Rozważ następującą odpowiedź JSON z backend API.

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

Zasób pomocniczy pod /userprofile/{userid} wygląda następująco:

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

Aby określić odpowiednie informacje o użytkowniku do uwzględnienia, usługa API Management musi określić, kim jest użytkownik końcowy. Ten mechanizm jest zależny od implementacji. W poniższym przykładzie użyto Subject oświadczenia tokenu JWT .

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

Usługa API Management przechowuje enduserid wartość w zmiennej kontekstowej do późniejszego użycia. Następnym krokiem jest ustalenie, czy poprzednie żądanie pobrało już informacje o użytkowniku i zapisano je w pamięci podręcznej. W tym celu usługa API Management używa cache-lookup-value polityki.

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

Uwaga / Notatka

Dodaj zasadę limitu szybkości (lub zasadę rate-limit-by-key) po wyszukiwaniu w pamięci podręcznej, aby pomóc ograniczyć liczbę wywołań i zapobiec przeciążeniu usługi zaplecza, jeśli pamięć podręczna nie jest dostępna.

Jeśli w pamięci podręcznej nie ma wpisu odpowiadającego wartości klucza, nie zostanie utworzona żadna zmienna kontekstowa userprofile. Usługa API Management sprawdza powodzenie wyszukiwania przy użyciu choose zasad przepływu sterowania.

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

Jeśli zmienna userprofile kontekstowa nie istnieje, usługa API Management będzie musiała wysłać żądanie HTTP, aby go pobrać.

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

Usługa API Management używa elementu enduserid , aby utworzyć adres URL zasobu profilu użytkownika. Gdy usługa API Management ma odpowiedź, pobiera tekst treści z odpowiedzi i zapisuje go z powrotem w zmiennej kontekstowej.

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

Aby uniknąć ponownego tworzenia tego żądania HTTP przez usługę API Management, gdy ten sam użytkownik wysyła inne żądanie, możesz określić, że profil użytkownika jest przechowywany w pamięci podręcznej.

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

Usługa API Management przechowuje wartość w pamięci podręcznej przy użyciu tego samego klucza, za pomocą którego usługa API Management pierwotnie próbowała go pobrać. Czas trwania, przez który usługa API Management decyduje się przechowywać wartość, powinien być oparty na tym, jak często zmieniają się informacje i jak tolerancyjni są użytkownicy na nieaktualne informacje.

Ważne jest, aby pamiętać, że pobieranie informacji z pamięci podręcznej jest nadal żądaniem sieciowym poza procesem i może dodać potencjalnie dziesiątki milisekund do czasu żądania. Korzyści pojawiają się, gdy określenie informacji o profilu użytkownika trwa dłużej niż pobranie danych z pamięci podręcznej, ze względu na konieczność wykonania zapytań do bazy danych lub agregowania informacji z wielu zapleczy.

Ostatnim krokiem procesu jest zaktualizowanie zwróconej odpowiedzi przy użyciu informacji o profilu użytkownika.

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

Możesz dołączyć znaki cudzysłowu jako część tokenu, aby nawet wtedy, gdy zastąpienie nie nastąpi, odpowiedź jest nadal prawidłowa w formacie JSON.

Po połączeniu tych kroków wynik końcowy jest zasadą, która wygląda podobnie do poniższej.

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

Takie podejście do buforowania jest stosowane głównie w witrynach internetowych, w których kod HTML jest skomponowany po stronie serwera, dzięki czemu może być renderowany jako pojedyncza strona. Może to być również przydatne w interfejsach API, w których klienci nie mogą wykonać buforowania HTTP na stronie klienta lub pożądane jest, aby nie obciążać klienta tą odpowiedzialnością.

Tego samego rodzaju buforowanie fragmentów można również wykonywać na serwerach internetowych zaplecza przy użyciu serwera buforowania Redis. Jednak użycie usługi API Management do wykonania tej pracy jest przydatne, gdy buforowane fragmenty pochodzą z różnych zapleczy niż odpowiedzi podstawowe.

Przezroczyste przechowywanie wersji

Częstą praktyką jest obsługa wielu różnych wersji implementacji interfejsu API w dowolnym momencie. Na przykład w celu obsługi różnych środowisk (deweloperskich, testowych, produkcyjnych itp.) lub obsługi starszych wersji interfejsu API w celu zapewnienia użytkownikom interfejsu API czasu na migrację do nowszych wersji.

Jednym z podejść do obsługi wielu wersji, zamiast wymagać od deweloperów klientów zmiany adresów URL z /v1/customers na /v2/customers, jest przechowywanie w danych profilu użytkownika, których wersji interfejsu API aktualnie chcą używać i wywoływać odpowiedni adres URL zaplecza. Aby określić prawidłowy adres URL zaplecza w celu wywołania określonego klienta, należy wykonać zapytanie dotyczące niektórych danych konfiguracji. W przypadku buforowania tych danych konfiguracji usługa API Management może zminimalizować wpływ na wydajność podczas wyszukiwania.

Pierwszym krokiem jest określenie identyfikatora użytego do skonfigurowania żądanej wersji. W tym przykładzie skojarzymy wersję z kluczem subskrypcji produktu.

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

Usługa API Management wykonuje wyszukiwanie w pamięci podręcznej, aby sprawdzić, czy pobrała już żądaną wersję klienta.

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

Uwaga / Notatka

Dodaj zasady limitu szybkości (lub zasady rate-limit-by-key ) po wyszukiwaniu pamięci podręcznej, aby pomóc ograniczyć liczbę wywołań i zapobiec przeciążeniu usługi zaplecza, jeśli pamięć podręczna nie jest dostępna.

Następnie usługa API Management sprawdza, czy nie znalazła go w pamięci podręcznej.

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

Jeśli usługa API Management nie znalazła go, usługa API Management pobierze go.

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

Wyodrębnij tekst treści odpowiedzi z odpowiedzi.

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

Zapisz go z powrotem w pamięci podręcznej do użytku w przyszłości.

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

Na koniec zaktualizuj adres URL zaplecza, aby wybrać wersję usługi żądanej przez klienta.

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

Pełne zasady są następujące:

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

To eleganckie rozwiązanie rozwiązuje wiele problemów z obsługą wersji interfejsu API, umożliwiając użytkownikom interfejsu API przezroczystą kontrolę nad wersją zaplecza, do której klienci uzyskują dostęp bez konieczności aktualizowania i ponownego wdrażania klientów.

Izolacja dzierżawy

W większych wdrożeniach wielodzierżawczych niektóre firmy tworzą oddzielne grupy klientów na różnych wdrożeniach sprzętowych zaplecza. Ta struktura minimalizuje liczbę klientów, których dotykają problemy ze sprzętem na zapleczu serwerów. Umożliwia również wdrażanie nowych wersji oprogramowania na etapach. W idealnym przypadku ta architektura zaplecza powinna być przezroczysta dla użytkowników interfejsu API. Tę przejrzystość można osiągnąć przy użyciu techniki podobnej do przezroczystego wersjonowania, manipulując adresem URL serwera przy użyciu stanu konfiguracji na klucz API.

Zamiast zwracać preferowaną wersję interfejsu API dla każdego klucza subskrypcyjnego, należy zwrócić identyfikator, który łączy dzierżawcę z przypisaną grupą sprzętową. Ten identyfikator może służyć do konstruowania odpowiedniego adresu URL zaplecza.

Podsumowanie

Swoboda używania pamięci podręcznej usługi Azure API Management do przechowywania dowolnego rodzaju danych umożliwia wydajny dostęp do danych konfiguracji, które mogą mieć wpływ na sposób przetwarzania żądania przychodzącego. Może również służyć do przechowywania fragmentów danych, które mogą rozszerzać odpowiedzi, zwracane z interfejsu API zaplecza.