نمط تجميع البوابة

Azure Traffic Manager

استخدم بوابة لتجميع طلبات فردية متعددة في طلب واحد. هذا النمط مفيدًا عندما يجب على العميل إجراء مكالمات متعددة إلى أنظمة خلفية مختلفة لتنفيذ عملية.

السياق والمشكلة

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

في الرسم التخطيطي التالي، يرسل العميل طلبات إلى كل خدمة (1,2,3). تعالج كل خدمة الطلب وترسل الاستجابة مرة أخرى إلى التطبيق (4,5,6). عبر شبكة جوال ذات زمن انتقال عال عادةً، فإن استخدام الطلبات الفردية بهذه الطريقة غير فعال وقد يؤدي إلى انقطاع الاتصالية أو طلبات غير مكتملة. في حين أن كل طلب قد يتم بالتوازي، يجب على التطبيق إرسال البيانات والانتظار ومعالجتها لكل طلب، كل ذلك على اتصالات منفصلة، مما يزيد من فرصة الفشل.

رسم تخطيطي للمشكلة لنمط تجميع البوابة

حل

استخدم بوابة لتقليل الدردشة بين العميل والخدمات. تقوم البوابة بتلقي طلبات العميل، ثم إرسال الطلبات إلى الأنظمة الخلفية المختلفة، ثم تجميع النتائج وإرسالها إلى العميل المطلوب.

يمكن أن يقلل هذا النمط من عدد الطلبات التي يقدمها التطبيق لخدمات الخلفية، وتحسين أداء التطبيق عبر الشبكات ذات زمن الانتقال العالي.

في الرسم التخطيطي التالي، يرسل التطبيق طلبًا إلى البوابة (1). يحتوي الطلب على حزمة من الطلبات الإضافية. تتحلل البوابة هذه وتعالج كل طلب عن طريق إرساله إلى الخدمة ذات الصلة (2). تقوم كل خدمة بإرجاع استجابة إلى البوابة (3). تجمع البوابة بين الاستجابات من كل خدمة وترسل الاستجابة إلى التطبيق (4). يقدم التطبيق طلبًا واحدًا ويتلقى استجابة واحدة فقط من البوابة.

رسم تخطيطي للحل لنمط تجميع البوابة

المسائل والاعتبارات

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

موعد استخدام النمط

استخدم هذا النمط عندما:

  • يحتاج العميل إلى الاتصال بخدمات خلفية متعددة لتنفيذ عملية.
  • قد يستخدم العميل شبكات ذات زمن انتقال كبير، مثل شبكات الجوال.

قد لا يكون هذا النمط مناسبًا عندما:

  • تريد تقليل عدد الاستدعاءات بين عميل وخدمة واحدة عبر عمليات متعددة. في هذا السيناريو، قد يكون من الأفضل إضافة عملية دُفعة إلى الخدمة.
  • يقع العميل أو التطبيق بالقرب من خدمات الواجهة الخلفية، وزمن الانتقال ليس عاملا مهما.

تصميم حمل العمل

يجب على المهندس المعماري تقييم كيفية استخدام نمط تجميع البوابة في تصميم حمل العمل الخاص بهم لمعالجة الأهداف والمبادئ التي تغطيها ركائز Azure Well-Architected Framework. على سبيل المثال:

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

- RE:07 أخطاء عابرة
تساعد قرارات تصميم الأمان على ضمان سرية بيانات وأنظمة حمل العمل وتكاملها وتوافرها. غالبا ما يقلل هذا المخطط من عدد نقاط اللمس التي يمتلكها العميل مع النظام، ما يقلل من مساحة السطح العام ونقاط المصادقة. يمكن أن تظل الخلفيات المجمعة معزولة تماما عن العملاء.

- تجزئة SE:04
- SE:08 تصلب
يساعد التميز التشغيلي على تقديم جودة حمل العمل من خلال العمليات الموحدة وتماسك الفريق. يتيح هذا النمط لمنطق الخلفية التطور بشكل مستقل عن العملاء، ما يسمح لك بتغيير تطبيقات الخدمة المتسلسلة، أو حتى مصادر البيانات، دون الحاجة إلى تغيير نقاط لمس العميل.

- OE:04 الأدوات والعمليات
تساعد كفاءة الأداء حمل العمل الخاص بك على تلبية الطلبات بكفاءة من خلال التحسينات في التحجيم والبيانات والرمز. يمكن أن يؤدي هذا التصميم إلى زمن انتقال أقل من التصميم الذي ينشئ فيه العميل اتصالات متعددة. التخزين المؤقت في تطبيقات التجميع يقلل من الاستدعاءات إلى أنظمة الواجهة الخلفية.

- PE:03 تحديد الخدمات
- أداء بيانات PE:08

كما هو الحال مع أي قرار تصميم، ضع في اعتبارك أي مفاضلات ضد أهداف الركائز الأخرى التي يمكن إدخالها مع هذا النمط.

مثال

يوضح المثال التالي كيفية إنشاء خدمة NGINX لتجميع بوابة بسيطة باستخدام Lua.

worker_processes  4;

events {
  worker_connections 1024;
}

http {
  server {
    listen 80;

    location = /batch {
      content_by_lua '
        ngx.req.read_body()

        -- read json body content
        local cjson = require "cjson"
        local batch = cjson.decode(ngx.req.get_body_data())["batch"]

        -- create capture_multi table
        local requests = {}
        for i, item in ipairs(batch) do
          table.insert(requests, {item.relative_url, { method = ngx.HTTP_GET}})
        end

        -- execute batch requests in parallel
        local results = {}
        local resps = { ngx.location.capture_multi(requests) }
        for i, res in ipairs(resps) do
          table.insert(results, {status = res.status, body = cjson.decode(res.body), header = res.header})
        end

        ngx.say(cjson.encode({results = results}))
      ';
    }

    location = /service1 {
      default_type application/json;
      echo '{"attr1":"val1"}';
    }

    location = /service2 {
      default_type application/json;
      echo '{"attr2":"val2"}';
    }
  }
}