نحوه اجرای وظایف پس‌زمینه در ASP.NET Core

مقدمه

پردازش فایل (مثلاً خروجی‌گیری یک ارائه به PDF) یک کار معمول سمت سرور است. انجام آن داخل هندلر درخواست (در حالی که مشتری منتظر است) معایب زیر را دارد:

  • رابط کاربری ضعیف. صفحه یخ می‌زند و کاربر باید برای نتیجه صبر کند. بارگذاری مجدد صفحه کار را لغو می‌کند.
  • زمان‌سنجی عملیات. نمی‌توانیم تضمین کنیم که پردازش در یک دوره ثابت تکمیل شود، بنابراین کاربر احتمالاً پیغام «زمان‌سنجی عملیات» را می‌بیند.
  • کارایی و مقیاس‌پذیری کم. ASP.NET Core برای پردازش تعداد زیادی درخواست به‌صورت ناهمزمان طراحی شده است. وظایف طولانی‌مدت و وابسته به CPU نخ‌ها را مسدود می‌کند و توان پردازش سرور را کاهش می‌دهد.
  • قابلیت تحمل خطا ضعیف. اگر در طول یک وظیفه طولانی‌مدت مشکلی پیش آید (مثلاً مشکل اتصال)، پردازش شکست می‌خورد و باید از ابتدا دوباره شروع شود.

یک رویکرد بهتر این است که کار را به‌صورت ناهمزمان زمان‌بندی کنید، آن را در پس‌زمینه پردازش کنید و وقتی آماده شد نتیجه را برگردانید.

در این مدل، کاربر می‌تواند وضعیت فعلی را ببیند (و می‌تواند صفحه را ترک یا بارگذاری مجدد کند)، منابع سرور می‌توانند به‌صورت کارآمد مقیاس‌پذیر و به‌صورت انعطاف‌پذیر تنظیم شوند و می‌توان سیاست retry را اعمال کرد.

یک راه‌حل معمول پردازش پس‌زمینه شامل موارد زیر است:

  1. یک API برای زمان‌بندی کار.
  2. یک API برای ردیابی وضعیت کار.
  3. یک worker پس‌زمینه برای پردازش کارهای زمان‌بندی‌شده.
  4. یک API برای ذخیره و بازیابی نتیجه.

مثال کار پس‌زمینه

برای نشان دادن این رویکرد، نمونه برنامه وب ASP.NET Core 3.1 را در نظر بگیرید. برنامه شامل صفحه‌ای است که کاربر می‌تواند یک ارائه را بارگذاری کند و روی Export to PDF کلیک کند؛ سپس ارائه بارگذاری شده و توسط یک worker پس‌زمینه به PDF تبدیل می‌شود.

برنامه وب

برنامه وب نمونه (پروژه BackgroundJobDemo) شامل موارد زیر است:

  • صفحه بارگذاری فایل (صفحه Razor «Upload»).
  • صفحه پیشرفت (صفحه Razor «Progress» با چند تابع JavaScript که وضعیت را بررسی و نمایش می‌دهند).
  • کنترلر (JobStatusController) که وضعیت پردازش را فراهم می‌کند (api/status/{jobId}).
  • کنترلر (JobResultController) که فایل PDF خروجی‌شده را باز می‌گرداند (api/result/{id}).
  • worker پس‌زمینه مبتنی بر سرویس میزبانی ASP.NET Core (به کلاس WorkerService مراجعه کنید).

صفحات Razor، کنترلرها و worker پس‌زمینه کار واقعی را از طریق اینترفیس‌های تعریف‌شده در پروژه BackgroundJobDemo.Common واگذار می‌کنند. پیاده‌سازی‌های واقعی مدیریت کار و پردازش در پروژه‌های جداگانه (BackgroundJobDemo.Local، BackgroundJobDemo.Aws و غیره) ارائه شده‌اند و می‌توانند در متد Startup.ConfigureServices سوئیچ شوند.

برای مقاصد نمایشی، صفحه «Upload» از مدل بایندینگ با بافر استفاده می‌کند، اما برای بارگذاری‌های بزرگ فایل، پخش بدون بافر پیشنهاد می‌شود. برای تولید، به جنبه‌های امنیتی مربوطه توجه کنید. صفحه «Progress» هر دو ثانیه یک بار وضعیت کار زمان‌بندی‌شده را با JavaScript می‌پرسد (این بازه قابل تنظیم است). پرس‌و‌جو معمول است، اما برای سناریوهای پیشرفته‌تر ممکن است به اعلان‌های زمان واقعی از طریق WebSockets نیاز داشته باشید (ارتباطات زمان واقعی خارج از محدوده این مقاله هستند). SignalR ابزار ساده اما قدرتمندی برای ارتباطات زمان واقعی است.

میزبانی worker پس‌زمینه در فرایند سرور برای برنامه‌های ساده راحت است اما معایبی دارد. رویکردی مقاوم‌تر و مقیاس‌پذیرتر این است که worker را در یک فرایند جداگانه مستقر کنید (به عنوان مثال، برنامه کنسولی BackgroundJobDemo.Worker را ببینید).

پیاده‌سازی پایه

پروژه BackgroundJobDemo.Local یک پیاده‌سازی ساده مدیریت کار را با استفاده از پایگاه داده SQLite ارائه می‌دهد (مسیر پایگاه داده از طریق LocalConfig.DbFilePath پیکربندی می‌شود؛ به Startup.ConfigureServices مراجعه کنید). فایل‌های بارگذاری‌شده و پردازش‌شده بر روی فایل‌سیستم ذخیره می‌شوند (مسیر پوشه ذخیره‌سازی از طریق LocalConfig.FileStorageFolderPath پیکربندی می‌شود؛ به Startup.ConfigureServices مراجعه کنید). برای تحمل خطای بهتر و عملکرد بهتر در برنامه‌های دنیای واقعی، زمان‌بندی کار باید از طریق صف‌های پیام (مثلاً RabbitMQ، AWS SQS، Azure Storage Queue) پیاده‌سازی شود.

پیاده‌سازی توزیع‌شده بر پایه خدمات وب آمازون

پروژه BackgroundJobDemo.Aws پردازش کار را بر روی Amazon Web Services اجرا می‌کند و معماری توزیع‌شده افقی مقیاس‌پذیر را نشان می‌دهد. این پروژه شامل مؤلفه‌های زیر است:

  • برنامه وب — با کاربر تعامل دارد و وظایف خروجی PPTX به PDF و غیره را زمان‌بندی می‌کند.
  • Worker — خروجی‌ها را پردازش می‌کند (درون‌فرایند، خارج از فرایند، یا AWS Lambda).
  • صف پیام — وظایفی را که باید پردازش شوند ذخیره می‌کند (Amazon SQS).
  • ذخیره‌سازی فایل — فایل‌های بارگذاری‌شده و پردازش‌شده را ذخیره می‌کند (Amazon S3).
  • ذخیره‌سازی کلید‑مقدار — وضعیت پردازش وظیفه را ردیابی می‌کند (Amazon DynamoDB).

یک معماری توزیع‌شده معمول بر پایه صف‌های پیام استوار است: برنامه وب کارهای پس‌زمینه را به صف اضافه می‌کند؛ یک worker پس‌زمینه وظایف را از صف بازیابی کرده و کار مورد نیاز را انجام می‌دهد. این کار مؤلفه‌ها را از هم جدا می‌کند و پردازش را به‌صورت ناهمزمان و قابل اعتماد می‌سازد. صف تحویل را تضمین می‌کند و از زمان‌پنهان‌سازی استفاده می‌کند: وقتی یک worker پیام را می‌گیرد، برای دیگر worker‌ها نامرئی می‌شود؛ تنها worker پردازشی آن را پس از تکمیل حذف می‌کند. اگر پردازش در زمان‌پنهان‌سازی پایان نیابد (مثلاً به‌دلیل خرابی یا مشکل شبکه)، پیام پردازش‌نشده دوباره قابل مشاهده می‌شود.

پیاده‌سازی ما از Amazon Simple Queue Service (SQS)، یک صف پیام کاملاً مدیریت‌شده برای میکروسرویس‌ها، سیستم‌های توزیع‌شده و برنامه‌های بدون سرور استفاده می‌کند.

صف‌های پیام برای پیام‌های سبک طراحی شده‌اند (مثلاً حداکثر اندازه پیام SQS برابر ۲۵۶ کیلوبایت است)، بنابراین یک پیام فقط باید توضیح وظیفه را داشته باشد. داده‌های سنگین (مانند فایل‌های قابل پردازش) باید به‌صورت جداگانه ذخیره و در پیام ارجاع داده شوند. برای ذخیره‌سازی فایل‌های بارگذاری‌شده و پردازش‌شده از Amazon S3 استفاده می‌شود.

یک ذخیره‌سازی کلید‑مقدار برای حفظ و بازیابی نتایج کار بر اساس شناسه لازم است. مثال از Amazon DynamoDB استفاده می‌کند، سرویس پایگاه داده NoSQL سریع و انعطاف‌پذیر.

برای اجرای برنامه نمونه با Amazon Web Services:

  1. در همان منطقه AWS، یک SQS queue، یک S3 bucket و یک جدول DynamoDB ایجاد و پیکربندی کنید:
    1. یک صف SQS،
    2. یک سطل S3،
    3. یک جدول DynamoDB.
  2. برنامه وب را به این سرویس‌ها متصل کنید با صدا زدن AddAws در Startup.ConfigureServices و ارائهٔ URL صف SQS، نام سطل S3، نام جدول DynamoDB و منطقه AWS.

منابع