كيفية تشغيل المهام الخلفية في ASP.NET Core

نظرة عامة

معالجة الملفات (مثل تصدير العرض التقديمي إلى PDF) هي مهمة نموذجية على الجهة الخلفية. معالجة الملفات البسيطة داخل معالج الطلبات (عندما ينتظر العميل بينما يقوم الخادم بالعمل) لها العيوب التالية:

  • واجهة مستخدم ضعيفة. يتجمد الصفحة ويجب على المستخدم الانتظار للحصول على النتيجة. إعادة تحميل الصفحة ستلغي المهمة.
  • مهلة العملية. لا يمكننا ضمان أن المعالجة ستكتمل في فترة زمنية ثابتة، مما يعني أن المستخدم سيرى “مهلة العملية” عاجلاً أو آجلاً.
  • انخفاض الإنتاجية وقابلية التوسع. تم تصميم ASP.NET Core لمعالجة العديد من الطلبات بشكل غير متزامن. المهام الطويلة التي تتطلب قدرات المعالجة تمنع سلاسل التنفيذ وتقلل من إنتاجية الخادم.
  • تحمل العطل السيئ. عندما يحدث خطأ ما في منتصف مهمة طويلة الأمد (مثل مشكلة الاتصال)، تفشل المعالجة ويتعين علينا إعادة تشغيل المعالجة من البداية مرة أخرى.

نهج أفضل هو جدولة المهمة بشكل غير متزامن في البداية، وإكمالها في الخلفية في ما بعد، وإرجاع نتيجة المعالجة في النهاية.

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

لذا، تتضمن الحلول النمطية لمعالجة الخلفية الأجزاء التالية:

  1. واجهة برمجة التطبيقات لجدولة المهمة.
  2. واجهة برمجة التطبيقات لتتبع حالة المهمة.
  3. العامل الخلفي لمعالجة المهام المجدولة.
  4. واجهة برمجة التطبيقات لتخزين/استرجاع النتيجة.

مثال على مهمة خلفية

لتوضيح هذا النهج، دعونا نConsiderمثال تطبيق ويب ASP.NET Core 3.1. يحتوي تطبيق الويب على صفحة ويب، حيث يمكن للمستخدم رفع عرض تقديمي، الضغط على زر “تصدير إلى PDF”، بعد ذلك سيتم رفع العرض التقديمي وتحويله إلى تنسيق PDF بواسطة عامل خلفي.

تطبيق الويب

يتضمن مثال تطبيق الويب (مشروع BackgroundJobDemo):

  • صفحة رفع الملفات (صفحة Razor تابعة للرفع).
  • صفحة التقدم (صفحة Razor للتقدم بها بعض دوال JavaScript للتحقق من الحالة وعرضها).
  • وحدة تحكم (JobStatusController) توفر حالة المعالجة (api/status/{jobId}).
  • وحدة تحكم (JobResultController) ترجع ملف PDF المصدّر (api/result/{id}).
  • عامل خلفي يعتمد على خدمة استضافة ASP.NET Core (انظر فئة WorkerService).

تقوم صفحات Razor ووحدات التحكم والعامل الخلفي بتفويض العمل الفعلي عبر واجهات، تم تعريفها في مشروع BackgroundJobDemo.Common. تم تعريف التنفيذات الدقيقة لإدارة المهام والمعالجة في المشاريع المنفصلة (BackgroundJobDemo.LocalBackgroundJobDemo.Aws وغيرها) ويمكن التبديل بينها بسهولة في طريقة Startup.ConfigureServices.

لأغراض العرض التوضيحي، تستخدم صفحة “الرفع” ربط النموذج المؤقت، ولكن لرفع الملفات الكبيرة يُوصى باستخدام تيار غير مؤقت. بالنسبة لنشر الإنتاج، يجب أخذ الجوانب الأمنية في الاعتبار. تقوم صفحة “التقدم” بالاستعلام عن حالة المهمة المجدولة عبر JavaScript كل ثانيتين (يمكن تعديل الفترة). يعتبر استعلام الحالة سلوكًا نموذجيًا، ولكن للحالات المتقدمة، قد تكون الإشعارات في الوقت الحقيقي (الاتصالات في الوقت الحقيقي خارج نطاق هذه المقالة) عبر WebSocket مطلوبة. SignalR أداة بسيطة ولكنها قوية للإشعارات في الوقت الحقيقي.

استضافة العامل الخلفي في عملية الخادم مفيدة للتطبيقات البسيطة، لكنها تحتوي على عيوب. الحل الأكثر قوة وقابلية للتوسع هو نشر العامل في عملية منفصلة (انظر على سبيل المثال تطبيق BackgroundJobDemo.Worker للوحدة). 

التنفيذ الأساسي

يحتوي مشروع BackgroundJobDemo.Local على تنفيذ بسيط لإدارة المهام باستخدام قاعدة بيانات SQLite (خيار ملف قاعدة البيانات محدد عبر LocalConfig.DbFilePath، انظر في Startup.ConfigureServices). يتم تخزين الملفات المرفوعة والمعالجة على نظام الملفات (خيار مجلد التخزين محدد عبر LocalConfig.FileStorageFolderPath، انظر في Startup.ConfigureServices). للحصول على تحمل أفضل للعطل وأداء أفضل في التطبيقات الواقعية، يجب تنفيذ جدولة المهام عبر قوائم الانتظار (مثل RabbitMQ، AWS SQS، Azure Storage Queue).

تنفيذ موزع استنادًا إلى خدمات الويب من Amazon

يقوم مشروع BackgroundJobDemo.Aws بتنفيذ معالجة المهام عبر خدمات الويب من Amazon ويظهر البنية الموزعة التي يمكن توسيعها بشكل أفقي. تتضمن المكونات التالية:

  • تطبيق ويب - يتفاعل مع المستخدم ويجدول مهام تصدير PPTX إلى PDF، إلخ.
  • عامل - يقوم بمعالجة التصدير (داخل العملية، خارج العملية أو Amazon Lambda).
  • قائمة انتظار الرسائل - تخزن المهام المراد معالجتها (Amazon SQS).
  • تخزين الملفات - يحتفظ بالملفات المرفوعة والمعالجة (Amazon S3).
  • تخزين مفتاح-قيمة - يوفر حالة معالجة المهمة (Amazon DynamoDB). 

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

تستخدم تطبيقنا خدمة Amazon Simple Queue (SQS) - قوائم انتظار الرسائل المدارة بالكامل لخدمات الميكروسيرفيس، والأنظمة الموزعة، والتطبيقات بدون خادم.

تم تصميم قوائم الانتظار للرسائل لتكون خفيفة الوزن (على سبيل المثال، حد حجم رسالة SQS هو 256 كيلو بايت)، لذا يجب أن تحتوي فقط على وصف المهمة. يجب وضع جميع البيانات الثقيلة (مثل الملفات المراد معالجتها) في تخزين منفصل والإشارة إليها من الرسالة. Amazon S3 هو تخزين كائنات مصمم لتخزين واسترجاع أي كمية من البيانات من أي مكان. يتم استخدام هذه الخدمة لتخزين الملفات المرفوعة والمعالجة.

يتطلب تخزين مفتاح-قيمة لتخزين واسترجاع نتيجة معالجة المهمة باستخدام المعرف. تم استخدام Amazon DynamoDB (خدمة قاعدة بيانات NoSQL سريعة ومرنة لأي حجم) في المثال.

لتشغيل تطبيق العرض التوضيحي باستخدام خدمات الويب من Amazon:

  1. إنشاء وتكوين في نفس منطقة AWS:
    1. قائمة انتظار SQS،
    2. حاوية S3،
    3. جدول DynamoDB.
  2. توصيل تطبيق الويب بالخدمات المنشأة باستخدام طريقة AddAws (الرابط URL لقائمة انتظار SQS، اسم حاوية S3، اسم جدول DynamoDB ومنطقة AWS) من Startup.ConfigureServices. 

المراجع