من خلال هذه المقالة ، سنشرح بشكل أساسي المشكلات التي واجهتها معالجة Servlet 3.0 غير المتزامنة في تطوير Java والحلول. فيما يلي المحتويات المحددة:
بدأت Servlet 3.0 في توفير AsyncContext لدعم المعالجة غير المتزامنة للطلبات. فما هي الفوائد التي يمكن أن تجلبها المعالجة غير المتزامنة للطلبات؟
بشكل عام ، فإن طريقة تعامل حاويات الويب هي تعيين مؤشر ترابط لكل طلب. نعلم جميعًا أن إنشاء المواضيع لا يخلو من التكلفة ، وأن مجموعة مؤشرات الترابط من حاويات الويب لها حد أعلى.
المشكلة التي يمكن التنبؤ بها للغاية هي أنه في ظل ظروف الحمل العالية ، يتم احتلال تجمع الخيوط ، لذلك لا يمكن انتظار الطلب اللاحق إلا. إذا لم تكن محظوظًا ، فسيقوم العميل بالإبلاغ عن خطأ في مهلة الانتظار.
قبل ظهور AsyncContext ، كانت الطريقة الوحيدة لحل هذه المشكلة هي توسيع تجمع مؤشرات الترابط في حاوية الويب.
ولكن لا تزال هناك مشكلة في هذا ، فكر في السيناريوهات التالية:
يوجد حاوية ويب بحجم تجمع مؤشرات الترابط 200. يوجد تطبيق ويب يحتوي على اثنين من servlets ، والوقت الذي يستغرقه Servlet-A للتعامل مع طلب واحد هو 10s ، والوقت الذي يستغرقه Servlet-B للتعامل مع طلب واحد هو 1s.
الآن نواجه حمولة عالية ، مع أكثر من 200 طلب إلى Servlet-A. إذا تم طلب Servlet-B في هذا الوقت ، فسننتظر لأن جميع مؤشرات ترابط HTTP قد احتلت Servlet-A.
في هذا الوقت ، اكتشف المهندس المشكلة ووسع حجم تجمع الخيوط إلى 400 ، ولكن استمر الحمل في الارتفاع. الآن هناك 400 طلب لـ Servlet-A ، ولا يزال Servlet-B لا يمكن الاستجابة.
هل رأيت المشكلة؟ نظرًا لأن مؤشر ترابط HTTP وخيط العمال يقترن معًا ، فسيتم ملء مؤشر ترابط HTTP عندما يتم تقديم عدد كبير من الطلبات لعملية تستغرق وقتًا طويلاً ، مما يؤدي إلى عدم قدرة حاوية الويب بأكملها على الاستجابة.
ومع ذلك ، إذا استخدمنا AsyncContext ، فيمكننا تسليم العملية المستهلكة للوقت إلى مؤشر ترابط آخر ، بحيث يتم إصدار مؤشر ترابط HTTP ويمكننا التعامل مع الطلبات الأخرى.
لاحظ أنه فقط باستخدام AsyncContext يمكنه تحقيق التأثير المذكور أعلاه. إذا كنت تستخدم مباشرة Thread () أو طرق مماثلة ، فلن يتم إرجاع مؤشر ترابط HTTP إلى الحاوية.
هنا مثال رسمي:
WebServlet (urlpatterns = {"/asyncservlet"} ، asyncsupported = true) يمتد asyncservlet الفئة العامة httpservlet {/ * ... نفس المتغيرات وطريقة init كما في syncservlet ... استجابة. Final AsyncContext acontext = request.startasync () ؛ acontext.start (new RunNable () {public void run () {string param = acontext.getRequest (). getParameter ("param") ؛ String runder = resource.process (param) ؛ httpservletresponse = acontext.getResponse () ؛ / * ... print to the rastel ... }} فخ
في هذا المثال الرسمي ، سيفتح كل مؤشر ترابط HTTP مؤشر ترابط عامل آخر لمعالجة الطلب ، ثم إرجاع مؤشر ترابط HTTP إلى حاوية الويب. ولكن انظر إلى javadoc لطريقة asynccontext.start ():
يؤدي إلى إرسال الحاوية خيطًا ، ربما من تجمع مؤشرات ترابط مُدار ، لتشغيل Runnable المحدد.
في الواقع ، لا يوجد تنظيم هنا من أين يأتي خيط العمال. ربما هو تجمع مؤشر ترابط آخر بخلاف تجمع مؤشر ترابط HTTP؟ أم أنها مجرد تجمع مؤشر ترابط HTTP؟
تقرأ المقالة المحدودة لمقالة asynccontext.start (): تحتوي حاويات الويب المختلفة على تطبيقات مختلفة لهذا ، ولكن Tomcat يستخدم فعليًا مجموعة مؤشرات الترابط HTTP للتعامل مع AsyncContext.start ().
هذا يعني أننا أردنا في الأصل إصدار مؤشر ترابط HTTP ، ولكن في الواقع لم يفعل ذلك ، لأن مؤشر ترابط HTTP لا يزال يستخدم كخيط العامل ، ولكن هذا الموضوع ليس هو نفسه مؤشر ترابط HTTP الذي يتلقى الطلب.
يمكننا أيضًا أن نرى هذا الاستنتاج من خلال معيار jmeter لـ asyncservlet1 و syncservlet ، ونتائج الإنتاجية المتشابهة. طريقة البدء: ابدأ رئيسيًا ، ثم استخدم jmeter لبدء تشغيل Benchmark.jmx (تجمع مؤشر ترابط HTTP = 200 تحت تكوين Tomcat الافتراضي).
باستخدام ExecutorService
لقد رأيت في وقت سابق أن Tomcat لا يحافظ على تجمع خيوط العمال بشكل منفصل ، لذلك يتعين علينا أن نجد طريقة للقيام بذلك بأنفسنا ، انظر Asyncservlet2 ، الذي يستخدم خدمة ExecutorService مع تجمع مؤشرات الترابط للتعامل مع AsyncContext.
طرق أخرى
لذلك ، لا توجد طريقة ثابتة لاستخدام AsyncContext. يمكنك استخدام طرق مختلفة للتعامل معها وفقًا للاحتياجات الفعلية. لهذا السبب ، تحتاج إلى بعض المعرفة بالبرمجة المتزامنة Java.
سوء فهم الأداء
الغرض من AsyncContext ليس تحسين الأداء ، ولا يوفر بشكل مباشر تحسين الأداء. إنه يوفر آلية لفصل مؤشر ترابط HTTP وخيط العمال ، وبالتالي تحسين استجابة حاويات الويب.
ومع ذلك ، يمكن لـ AsyncContext تحسين الأداء في مرحلة ما ، ولكن هذا يعتمد على كيفية كتابة الكود الخاص بك.
على سبيل المثال: يبلغ عدد تجمعات مؤشرات ترابط HTTP في حاوية ويب 200 ، ويستخدم Servlet مجموعة مؤشرات ترابط 300 عامل للتعامل مع AsyncContext.
بالمقارنة مع مجموعة مؤشرات ترابط Sync Method Worker = HTTP Pool = 200 ، في هذه الحالة ، لدينا مجموعة مؤشرات ترابط العمال 300 ، لذلك سيحقق بالتأكيد بعض التحسينات في الأداء (بعد كل شيء ، هناك المزيد من الأشخاص الذين يعملون).
على العكس من ذلك ، إذا كان عدد مؤشرات ترابط العامل هو <= عدد مؤشرات ترابط HTTP ، فلن يكون هناك أي تحسين للأداء ، لأن عنق الزجاجة لمعالجة طلبات المعالجة في مؤشر ترابط العمال.
يمكنك تعديل حجم تجمع مؤشرات الترابط من asyncservlet2 ومقارنته مع نتائج SyncServlet القياسية للتحقق من هذا الاستنتاج.
لا تظن أن تجمع مؤشرات ترابط العمال يجب أن يكون أكبر من تجمع مؤشرات الترابط HTTP. الأسباب كما يلي:
مسؤوليتان مختلفتان. أحدهما هو أن حاوية الويب تستخدم لتلقي الطلبات الخارجية ، والآخر هو معالجة منطق العمل.
إنشاء المواضيع يأتي بتكلفة. إذا كان تجمع مؤشرات ترابط HTTP كبيرًا بالفعل ، فإن إنشاء تجمع مؤشر ترابط أكبر للعامل سيؤدي إلى زيادة كبيرة في مفتاح السياق والنفقات العامة للذاكرة.
الغرض من AsyncContext هو إطلاق مؤشر ترابط HTTP لتجنب الاستخدام طويل الأجل للعمليات وبالتالي التسبب في عدم قدرة حاوية الويب على الاستجابة.
لذلك في معظم الحالات ، لن يكون تجمع خيوط العمال كبيرًا جدًا ، وسيتم بناء تجمعات خيوط مختلفة للعاملين وفقًا لشركات مختلفة.
على سبيل المثال: يبلغ حجم تجمع مؤشرات ترابط حاوية الويب 200 ، وحجم تجمع مؤشرات ترابط العامل 10 للخدمة البطيئة. وبهذه الطريقة ، بغض النظر عن عدد الطلبات المستخدمة لإبطاء العمليات ، فلن تملأ مؤشر ترابط HTTP ويتسبب في عدم قدرة الطلبات الأخرى على معالجتها.