Bagikan melalui


Cache kustom di Azure API Management

BERLAKU UNTUK: Semua tingkatAN API Management

Layanan Azure API Management memiliki dukungan bawaan untuk cache respons HTTP menggunakan URL sumber daya sebagai kunci. Kunci dapat dimodifikasi oleh header permintaan menggunakan properti vary-by. Ini berguna untuk men-cache seluruh respons HTTP (juga dikenal sebagai representasi), tetapi terkadang berguna untuk hanya men-cache sebagian dari representasi. Kebijakan cache-lookup-value dan cache-store-value memberikan kemampuan untuk menyimpan dan mengambil bagian data yang berubah-ubah dari dalam definisi kebijakan. Kemampuan ini juga menambah nilai pada kebijakan kirim-permintaan karena Anda dapat menyimpan tanggapan dari layanan eksternal dalam cache.

Sistem

Layanan API Management menggunakan cache data internal per-penyewa yang dibagikan sehingga, saat Anda meningkatkan skala ke beberapa unit, Anda masih mendapatkan akses ke data cache yang sama. Namun, ketika bekerja dengan penyebaran multi-wilayah, terdapat cache independen di masing-masing wilayah. Penting untuk tidak memperlakukan cache sebagai penyimpan data, yang merupakan satu-satunya sumber informasi. Jika Anda melakukannya, dan kemudian memutuskan untuk memanfaatkan penerapan multi-wilayah, maka pelanggan dengan pengguna yang melakukan perjalanan dapat kehilangan akses ke data cache tersebut.

Catatan

Tembolok internal tidak tersedia di tingkat Konsumsi API Management Azure. Sebagai gantinya, Anda dapat menggunakan Azure Cache for Redis eksternal. Cache eksternal memungkinkan kontrol dan fleksibilitas cache yang lebih besar untuk instans API Management di semua tingkatan.

Caching fragmen

Ada kasus-kasus tertentu di mana tanggapan yang dikembalikan berisi beberapa bagian data yang mahal untuk ditentukan dan namun tetap segar untuk jumlah waktu yang wajar. Sebagai contoh, pertimbangkan layanan yang dibangun oleh maskapai yang memberikan informasi yang berkaitan dengan reservasi penerbangan, status penerbangan, dan sebagainya. Jika pengguna adalah anggota program poin maskapai, mereka juga akan memiliki informasi yang berkaitan dengan status mereka saat ini dan akumulasi mileage. Informasi terkait pengguna ini mungkin disimpan dalam sistem yang berbeda, tetapi mungkin diinginkan untuk memasukkannya sebagai tanggapan yang dikembalikan tentang status penerbangan dan reservasi. Ini dapat dilakukan menggunakan proses yang disebut fragment caching. Representasi utama dapat dikembalikan dari server asal menggunakan semacam token untuk menunjukkan di mana informasi terkait pengguna akan dimasukkan.

Pertimbangkan respons JSON berikut dari API backend.

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

Dan sumber daya sekunder di /userprofile/{userid} yang terlihat seperti,

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

Untuk menentukan informasi pengguna yang sesuai untuk disertakan, API Management perlu mengidentifikasi siapa pengguna akhir. Mekanisme ini tergantung pada implementasi. Contoh berikut menggunakan klaim Subject dari token JWT.

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

API Management menyimpan nilai enduserid dalam variabel konteks untuk digunakan nanti. Langkah selanjutnya adalah menentukan apakah permintaan sebelumnya telah mengambil informasi pengguna dan menyimpannya di cache. Untuk ini, API Management menggunakan kebijakan cache-lookup-value tersebut.

<cache-lookup-value
key="@("userprofile-" + context.Variables["enduserid"])"
variable-name="userprofile" />

Jika tidak ada entri dalam cache yang sesuai dengan nilai kunci, maka tidak ada variabel konteks userprofile yang dibuat. API Management memeriksa keberhasilan pencarian menggunakan kebijakan aliran kontrol 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>

Jika variabel konteks userprofile tidak ada, maka API Management harus membuat permintaan HTTP untuk mengambilnya.

<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 menggunakan enduserid untuk membuat URL ke sumber daya profil pengguna. Setelah API Management memiliki respons, ia menarik teks isi keluar dari respons dan menyimpannya kembali ke variabel konteks.

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

Untuk menghindari API Management membuat permintaan HTTP ini lagi, ketika pengguna yang sama membuat permintaan lain, Anda dapat menentukan untuk menyimpan profil pengguna di cache.

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

API Management menyimpan nilai dalam cache menggunakan kunci yang sama dengan yang awalnya digunakan oleh Manajemen API untuk mengambilnya. Durasi yang dipilih API Management untuk menyimpan nilai harus didasarkan pada seberapa sering informasi berubah dan seberapa toleran pengguna terhadap informasi yang kedaluwarsa.

Penting untuk disadari bahwa mengambil dari cache masih merupakan permintaan jaringan di luar proses dan berpotensi dapat menambah puluhan milidetik ke permintaan. Manfaat datang ketika menentukan informasi profil pengguna membutuhkan waktu lebih lama karena perlu melakukan kueri database atau mengumpulkan informasi dari beberapa back-ends.

Langkah terakhir dalam proses ini adalah memperbarui respons yang dikembalikan dengan informasi profil pengguna.

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

Anda dapat memilih untuk menyertakan tanda kutip sebagai bagian dari token sehingga meskipun penggantian tidak terjadi, responsnya masih berupa JSON yang valid.

Setelah Anda menggabungkan langkah-langkah ini, hasil akhirnya adalah kebijakan yang terlihat seperti berikut ini.

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

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

Pendekatan penembolokan ini terutama digunakan di situs web di mana HTML disusun di sisi server sehingga dapat dirender sebagai satu halaman. Ini juga dapat berguna di API di mana klien tidak dapat melakukan penembolokan HTTP sisi klien atau diinginkan untuk tidak meletakkan tanggung jawab itu pada klien.

Cache fragmen yang sama ini juga dapat dilakukan di server web backend menggunakan server cache Redis, namun, menggunakan layanan API Management untuk melakukan pekerjaan ini berguna saat fragmen yang di-cache berasal dari back-end yang berbeda dari respons utama.

Pembuatan versi transparan

Sudah menjadi praktik umum untuk mendukung beberapa versi implementasi API yang berbeda pada satu waktu. Misalnya, untuk mendukung lingkungan yang berbeda (dev, test, production, dll.) atau untuk mendukung versi API yang lebih lama untuk memberikan waktu bagi konsumen API untuk bermigrasi ke versi yang lebih baru.

Salah satu pendekatan untuk menangani hal ini, alih-alih mengharuskan pengembang klien untuk mengubah URL dari /v1/customers ke /v2/customers adalah menyimpan dalam data profil konsumen versi API mana yang saat ini ingin mereka gunakan dan memanggil URL backend yang sesuai. Untuk menentukan URL backend yang benar untuk memanggil klien tertentu, perlu untuk menanyakan beberapa data konfigurasi. Dengan mem-caching data konfigurasi ini, API Management dapat meminimalkan penalti kinerja dalam melakukan pencarian ini.

Langkah pertama adalah menentukan pengidentifikasi yang digunakan untuk mengonfigurasi versi yang diinginkan. Dalam contoh ini, saya memilih untuk mengaitkan versi ke kunci langganan produk.

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

API Management kemudian melakukan pencarian cache untuk melihat apakah sudah mengambil versi klien yang diinginkan.

<cache-lookup-value
key="@("clientversion-" + context.Variables["clientid"])"
variable-name="clientversion" />

Kemudian, API Management memeriksa untuk melihat apakah tidak menemukannya di cache.

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

Jika API Management tidak menemukannya, API Management akan mengambilnya.

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

Ekstrak teks isi respons dari respons.

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

Simpan kembali di cache untuk digunakan di masa mendatang.

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

Dan akhirnya perbarui URL back-end untuk memilih versi layanan yang diinginkan oleh klien.

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

Kebijakan lengkap terlihat sebagai berikut:

<inbound>
    <base />
    <set-variable name="clientid" value="@(context.Subscription.Key)" />
    <cache-lookup-value key="@("clientversion-" + context.Variables["clientid"])" variable-name="clientversion" />

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

Memungkinkan konsumen API untuk secara transparan mengontrol versi backend mana yang sedang diakses oleh klien tanpa harus memperbarui dan menebus klien adalah solusi elegan yang membahas banyak masalah pembuatan versi API.

Isolasi Penyewa

Dalam penyebaran multi-penyewa yang lebih besar, beberapa perusahaan membuat grup penyewa terpisah pada penyebaran perangkat keras backend yang berbeda. Ini meminimalkan jumlah pelanggan yang terkena dampak masalah perangkat keras pada backend. Ini juga memungkinkan versi perangkat lunak baru diluncurkan secara bertahap. Idealnya arsitektur backend ini harus transparan bagi konsumen API. Ini dapat dicapai dengan cara yang sama dengan penerapan versi transparan karena didasarkan pada teknik yang sama untuk memanipulasi URL backend menggunakan status konfigurasi per kunci API.

Alih-alih mengembalikan versi API pilihan untuk setiap kunci langganan, Anda akan mengembalikan pengidentifikasi yang menghubungkan penyewa ke grup perangkat keras yang ditetapkan. Pengidentifikasi tersebut dapat digunakan untuk membuat URL backend yang sesuai.

Ringkasan

Kebebasan untuk menggunakan cache Azure API Management untuk menyimpan segala jenis data memungkinkan akses efisien ke data konfigurasi yang dapat memengaruhi cara permintaan masuk diproses. Ini juga dapat digunakan untuk menyimpan fragmen data yang dapat menambah respons, dikembalikan dari API backend.