التخزين المؤقت المخصص في خدمة Azure API Management

ينطبق على: جميع مستويات إدارة واجهة برمجة التطبيقات

خدمة Azure API Management تشتمل على دعم مضمن لميزة التخزين المؤقت لاستجابة البروتوكول HTTP من خلال عنوان URL الخاص بالمورد بصفته المفتاح. ويمكن تعديل هذا المفتاح باستخدام رؤوس الطلبات من خلال خصائص vary-by. يمكن الاستفادة من هذا في التخزين المؤقت لاستجابات البروتوكول HTTP بأكملها (يُعرف أيضًا باسم تمثيلات)، ولكن في بعض الأحيان يكون من المفيد تخزين جزء من التمثيل فحسب. يوفّر النهجان cache-lookup-value وcache-store-value إمكانية تخزين واسترداد أجزاء عشوائية من البيانات من داخل تعريفات السياسات. تضيف هذه الإمكانية أيضًا قيمة إلى نهج send-request، ويرجع ذلك إلى أنّ بإمكانك الآن تخزين الاستجابات مؤقتًا من الخدمات الخارجية.

بناء الأنظمة

إنّ خدمة API Management تستخدم ذاكرة تخزين داخلي مؤقت مشترَكة لكل مستأجر حتى تتمكّن من الوصول إلى البيانات المخزنة مؤقتًا نفسها في أثناء توسيع نطاقها إلى عدة وحدات. ومع ذلك، عند إجراء عملية نشر متعدد المناطق، يكون هناك بيانات مخزنة مؤقتًا ومستقلة داخل كل منطقة من المناطق. من المهم عدم التعامل مع ذاكرة التخزين المؤقت كمخزن بيانات، إذ إنّها المصدر الوحيد لبعض المعلومات. في حال سبق لك تنفيذ ذلك، وقررت لاحقًا الاستفادة من النشر متعدد المناطق، فقد يفقد العملاء الذين لديهم مستخدمين يسافرون إمكانية الوصول إلى تلك البيانات المخزنة مؤقتًا.

إشعار

ذاكرة التخزين المؤقت الداخلية غير متوفرة في مستوى الاستهلاك من Azure API Management. ويمكنك استخدام Azure Cache for Redis خارجية بدلاً من ذلك. تسمح ذاكرة التخزين المؤقت الخارجية بمزيد من التحكم في ذاكرة التخزين المؤقت والمرونة لمثيلات APIM في جميع المستويات.

التخزين المؤقت للأجزاء

هناك حالات معينة تحتوي فيها الردود التي ترد على جزء من البيانات والتي يكون تحديدها مكلفًا، ولكنّها تظل حديثة لفترة زمنية معقولة. على سبيل المثال، ضع في اعتبارك خدمة تم إنشاؤها بواسطة شركة طيران توفر معلومات تتعلق بحجوزات الرحلات الجوية وحالة الرحلة وما إلى ذلك. إذا كان المستخدم عضوًا في برنامج نقاط الخطوط الجوية، فسيكون لديه أيضًا معلومات تتعلق بوضعه الحالي والأميال المتراكمة. ويمكن أن تكون هذه المعلومات المتعلقة بالمستخدم مخزّنة في نظام مختلف، ولكن قد يكون من المستحسن إدراجها في الردود التي يتم إرجاعها بشأن حالة الرحلة والحجوزات. ويمكن تنفيذ ذلك من خلال عملية تُعرف باسم التخزين المؤقت للأجزاء. يمكن إرجاع التمثيل الأساسي من الملقم الأصل باستخدام نوع معيّن من الرموز المميزة بهدف الإشارة إلى مكان إدراج المعلومات المتعلقة بالمستخدم.

خذ بعين الاعتبار استجابة JSON التالية من واجهة برمجة تطبيقات خلفية.

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

ومورد ثانوي في /userprofile/{userid} يتشابه معه،

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

ولتحديد معلومات المستخدم المناسبة المطلوب تضمينها، تحتاج خدمة API Management إلى تحديد هوية المستخدم النهائي. وتعتمد هذه الآلية على التنفيذ. يستخدم Subject المثال التالي مطالبة JWT رمز مميز.

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

تُخزّن خدمة API Management القيمة enduserid في متغير سياق للاستخدام لاحقًا. الخطوة التالية هي تحديد ما إذا كان هناك طلب سابق أجرى فعلاً استرداد معلومات المستخدم وتخزينها في ذاكرة التخزين المؤقت. ولهذا السبب، تستخدم خدمة API Management السياسة cache-lookup-value.

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

وإذا لم يكن هناك إدخال في ذاكرة التخزين المؤقت التي تتوافق مع قيمة المفتاح، لا يتم userprofile إنشاء أي متغير سياق. وتتحقّق خدمة API Management من نجاح علمية البحث باستخدام السياسة 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>

إذا userprofile لم يكن متغير السياق متوفّرًا، فستجري خدمة API Management طلب HTTP لاسترداده.

<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 enduserid لإنشاء عنوان URL في المورد الخاص بملف تعريف المستخدم. وبعد حصول خدمة API Management على الاستجابة، تسحب النص الأساسي من الاستجابة وتخزّنه مرة أخرى في شكل متغير سياق.

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

لمنع خدمة API Management من إجراء طلب HTTP هذا مرة أخرى، يمكنك تحديد التخزين لملف تعريف المستخدم في ذاكرة التخزين المؤقت، وذلك عندما يقوم المستخدم نفسه بإجراء طلب آخر.

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

تُخزن خدمة API Management القيمة في ذاكرة التخزين المؤقت باستخدام المفتاح الذي حاولت API Management استرداده به. يجب أن تكون المدة التي تختارها خدمة API Management لتخزين القيمة مستنِدة إلى عدد المرات التي تتغير فيها المعلومات ومدى تقبل المستخدمين للمعلومات القديمة.

من المهم معرفة أن الاسترداد من ذاكرة التخزين المؤقت لا يزال خارج نطاق العملية، طلب شبكة الاتصال، ويُحتمل أن يكون الاسترداد بإمكانه إضافة عشرات المللي ثانية إلى الطلب. تتجلّى الفوائد عندما تتطلب عملية تحديد معلومات ملف تعريف المستخدم وقتًا أطول من ذلك بسبب الحاجة إلى إجراء استعلامات خاصة بقواعد البيانات أو تجميع المعلومات من أطراف خلفية متعددة.

الخطوة الأخيرة في العملية هي تحديث الاستجابة التي تم إرجاعها بمعلومات ملف تعريف المستخدم.

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

يمكنك اختيار تضمين علامات الاقتباس كجزء من الرمز المميز حتى تظل استجابة JSON صالحة حتى في حال عدم حدوث الاستبدال.

بعد دمج كل هذه الخطوات، تكون النتيجة النهائية هي سياسة تشبه السياسة التالية.

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

يتم استخدام نهج التخزين المؤقت هذا بصورة أساسية في مواقع الويب التي يتم فيها إنشاء HTML على الخادم ليتم عرضه كصفحة واحدة. كما يمكن أن يكون مفيدًا في واجهات برمجة التطبيقات التي لا يتوفّر للعملاء فيها إجراء التخزين المؤقت لبروتوكول HTTP من جانب العميل أو أنّه من المستحسن عدم وضع هذه المسؤولية على العميل.

وهذا النوع نفسه من التخزين المؤقت جزء يمكن أيضًا أن يتم على ملقمات الويب الخلفية باستخدام ملقم التخزين المؤقت Redis. ومع ذلك، يُعد استخدام خدمة API Management لتنفيذ هذا العمل مفيدًا عندما يتم الحصول على الأجزاء المخزنة مؤقتًا من نهايات خلفية تختلف عن الاستجابات الأساسية.

إنشاء إصدار يتسم بالشفافية

من الممارسات الشائعة دعم إصدارات تنفيذ مختلفة متعددة من واجهة برمجة التطبيقات في أي وقت. على سبيل المثال، لدعم بيئات مختلفة (التطوير والاختبار والإنتاج وغيرها) أو لدعم الإصدارات القديمة من API بهدف إتاحة المزيد من الوقت لمستخدمي API ليتمكّنوا من الترحيل إلى إصدارات أحدث.

هناك نهج واحد لمعالجة هذا، بدلاً من مطالبة العملاء من مطوري البرامج بتغيير عناوين URL من /v1/customers إلى /v2/customers هو تخزين إصدار API المطلوب استخدامه في الوقت الحالي في بيانات الملف الشخصي للمستهلك واستدعاء عنوان URL الخلفي المناسب. ولتحديد عنوان URL الخلفي الصحيح لاستدعاء عميل معين، يجب الاستعلام عن بعض بيانات التكوين. عن طريق التخزين المؤقت لبيانات التكوين هذه، يمكن لخدمة API Management تقليل عقوبة الأداء بسبب تنفيذ عملية البحث هذه.

الخطوة الأولى هي تحديد المعرِّف المستخدَم لتكوين الإصدار المطلوب. في هذا المثال، تم اختيار ربط الإصدار بمفتاح الاشتراك في المنتج.

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

تُنفّذ خدمة API Management بعد ذلك علمية بحث في ذاكرة التخزين المؤقت لمعرفة ما إذا كان تم استرداد إصدار العميل المطلوب.

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

بعد ذلك، تتحقق APIM لمعرفة ما إذا لم تجدها في ذاكرة التخزين المؤقت.

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

تسترد خدمة API Management إصدار العميل في حال عدم العثور عليه.

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

استخرِج نص الاستجابة من الاستجابة.

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

خزِّنه مرة أخرى في ذاكرة التخزين المؤقت لتتمكن من استخدامه في المستقبل.

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

وأخيرًا، حدِّث عنوان URL الخلفي لتحديد إصدار الخدمة المطلوبة من قِبَل العميل.

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

السياسة الكاملة هي كما يلي:

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

تمكين مستخدمي خدمة API Management من التحكم بشفافية في الإصدار الخلفي الذي يتم الوصول إليه من قِبلَ العملاء بدون الحاجة إلى تحديث، وتُعدّ إعادة توزيع العملاء حلاً ملائمًا يعالج العديد من مخاوف إنشاء إصدار API.

عزل المستأجر

في عمليات النشر الكبيرة والمتعددة المستأجرين، تُنشئ بعض الشركات مجموعات منفصلة من المستأجرين على عمليات نشر متميزة للأجهزة الخلفية. وهذا يقلل من عدد العملاء الذين يتأثرون بمشاكل الأجهزة على الخلفية. ويتيح ذلك أيضًا نشر إصدارات البرامج الجديدة على مراحل. من الناحية المثالية، يجب أن تكون بنية الخلفية شفافة لمستخدمي API Management. يمكن تحقيق ذلك بطريقة مشابهة للتكوين الشفاف لأنه يستند إلى الأسلوب نفسه لمعالجة عنوان URL الخلفي باستخدام حالة التكوين لكل مفتاح من مفاتيح API.

وبدلاً من إرجاع إصدار مفضَّل من API لكل مفتاح اشتراك، يمكنك إرجاع معرّف يربط المستأجر بمجموعة الأجهزة المخصّصة. ويمكن استخدام هذا المعرف لإنشاء عنوان URL الخلفي المناسب.

الملخص

تتيح حرية استخدام التخزين المؤقت في خدمة Azure API management لتخزين أي نوع من البيانات إمكانية الوصول الفعال إلى بيانات التكوين التي يمكن أن تؤثر على طريقة معالجة الطلب الوارد. ويمكن استخدامها أيضًا لتخزين أجزاء البيانات التي يمكن أن تزيد من عدد الاستجابات، التي تم إرجاعها من الواجهة الخلفية لـ API.