چطور کارهای پسزمینه را در یک اپ وب اجرا کنیم: صفها، زمانبندها و cron
برای اجرای کارهای پسزمینه در یک اپ وب، ابتدا کار را به دو نوع تقسیم کنید. کارهای راهاندازشده (مثل ارسال ایمیل خوشآمد، پردازش یک آپلود) میتوانند در یک صف کار ذخیره شوند، که اغلب یک جدول در پایگاه داده شماست و یک worker با تلاش مجدد آن را انجام میدهد. کارهای زمانبندیشده (گزارش شبانه، یادآور هر ۱۵ دقیقه) به چیزی نیاز دارند که طبق برنامه شلیک شود: قابلیت cron میزبان، یا یک زمانبند بیرونی که یک endpoint محافظتشده در اپ شما را صدا میزند. همیشه کارها را طوری بسازید که اجرای دوبارهشان بیخطر باشد، شکستها را ثبت کنید، و هرگز به تایمری داخل سروری که ممکن است بخوابد یا ریاستارت شود تکیه نکنید.
این راهنما گزینهها، زمان مناسب هر کدام، و چگونگی راهاندازی آنها با یک ساز اپلیکیشن هوش مصنوعی را توضیح میدهد، از جمله روی میزبانهایی که تا صفر مقیاس میشوند.
چه چیزی کار پسزمینه حساب میشود
هر کاری که کند، غیرقابلاعتماد است یا برای پاسخ فوری کاربر لازم نیست:
- ارسال ایمیل، اعلان و رسید
- تغییر اندازه تصاویر، تبدیل ویدیو، ساختن PDF
- فراخوانی APIهای کند شخص ثالث، از جمله مدلهای هوش مصنوعی برای کارهای طولانی
- وارد کردن یا همگامسازی داده از یک سیستم دیگر
- پاکسازی شبانه، گزارشها و یادآورها
انجام اینها داخل خود درخواست، صفحات را کند و شکننده میکند: اگر سرویس ایمیل از کار بیفتد، ثبتنام کاربر شکست میخورد. انتقال آن به پسزمینه یعنی کاربر پاسخ فوری میگیرد و کار میتواند دوباره امتحان شود.
گزینهها
| گزینه | نحوه کار | مزایا | معایب |
|---|---|---|---|
| شلیکوفراموش در همان فرایند | پاسخ بده، سپس کار را در سرور ادامه بده | بدون راهاندازی | اگر سرور متوقف شود از بین میرود؛ بدون تلاش مجدد |
تایمر داخل فرایند (setInterval، setTimeout) | یک تایمر در سرور در حال اجرا | ساده | وقتی خوابیده اجرا نمیشود؛ با چند نسخه تکرار میشود |
| صف پشتیبانیشده با پایگاه داده | کارها به صورت ردیف ذخیره میشوند؛ یک worker آنها را برمیدارد و اجرا میکند | از پایگاه دادهای که دارید استفاده میکند؛ ماندگار است؛ بررسیاش آسان است | تلاش مجدد و قفلگذاری را خودتان میسازید؛ چیزی برای اجرای worker لازم است |
| صف اختصاصی (مبتنی بر Redis یا مدیریتشده) | یک سرویس صف بهعلاوه workerها | قابلیتهای بالغ تلاش مجدد و زمانبندی | یک سرویس دیگر برای اجرا و پرداخت |
| سرویس کار یا workflow میزبانیشده | یک شخص ثالث با تلاش مجدد و برنامهها endpointهای شما را صدا میزند | زمانبندی و تلاش مجدد توسط آنها مدیریت میشود | وابستگی به فروشنده؛ یک حساب و کلید دیگر |
| زمانبند بیرونی که یک endpoint را صدا میزند | یک تایمر جای دیگر طبق برنامه یک درخواست HTTP میفرستد | روی هر میزبانی کار میکند، از جمله تا صفر مقیاسشونده | حداقل فاصلهها و تأخیرها؛ endpoint را محافظت کنید |
| cron داخلی میزبان | پلتفرم یک دستور را طبق برنامه اجرا میکند | در جاهایی که وجود دارد ساده است | همه میزبانها آن را ارائه نمیدهند |
پاسخ واحد و درستی وجود ندارد. یک اپ کوچک اغلب فقط به یک صف پایگاه داده بهعلاوه یک زمانبند بیرونی نیاز دارد.
چرا میزبانهایی که تا صفر مقیاس میشوند تصویر را عوض میکنند
بسیاری از میزبانهای مدرن وقتی کسی از اپ استفاده نمیکند آن را به خواب میبرند و با درخواست بعدی بیدارش میکنند. این پول را صرفهجویی میکند، اما یک سرور خوابیده هیچ کدی اجرا نمیکند: تایمرها شلیک نمیشوند و workerها سرکشی نمیکنند. روی این میزبانها، کار زمانبندیشده باید با یک درخواست ورودی شروع شود، و سادهترین منبع آن درخواست یک زمانبند بیرونی است. هر فراخوانی اپ را بیدار میکند (و اغلب پایگاه دادهاش را)، پس صدا زدن هر دقیقه بیشتر از هر ۱۵ دقیقه هزینه دارد. کندترین برنامهای را انتخاب کنید که نیاز را برآورده کند.
الگوی ۱: یک صف کار با پایگاه داده
برای کارهای راهاندازشده:
۱. یک جدول jobs بسازید: نوع، payload (JSON)، وضعیت (pending، running، done، failed)، تعداد تلاشها، run_after، آخرین خطا، زمانها.
۲. وقتی اتفاقی میافتد، یک ردیف job را در همان تراکنشی که تغییر را ایجاد کرده وارد کنید، تا با هم موفق یا با هم شکست بخورند.
۳. یک worker کارهای سررسیده را برمیدارد، آنها را running علامت میزند، کار را انجام میدهد و done علامت میزند.
۴. در صورت شکست، attempts را زیاد کنید، run_after را دیرتر تنظیم کنید (مثلاً ۱، ۵ و سپس ۳۰ دقیقه) و خطا را ذخیره کنید. پس از حداکثر تعداد تلاش، آن را failed علامت بزنید و به کسی هشدار دهید.
اگر ممکن است بیش از یک worker همزمان اجرا شود، کارها باید به شکل ایمن برداشته شوند. در Postgres، SELECT ... FOR UPDATE SKIP LOCKED به هر worker اجازه میدهد ردیفهای متفاوتی را بدون برخورد با هم بردارد.
چه چیزی worker را اجرا میکند؟ روی سروری که همیشه روشن است، یک حلقه در یک فرایند جداگانه. روی میزبانی که تا صفر مقیاس میشود، endpoint زمانبندیشده در الگوی ۲ میتواند در هر فراخوانی یک دسته از کارهای سررسیده را پردازش کند.
الگوی ۲: یک زمانبند بیرونی که اپ شما را صدا میزند
برای کارهای زمانبندیشده:
۱. یک endpoint مثل POST /api/cron/send-reminders اضافه کنید.
۲. محافظتش کنید: یک توکن مخفی در هدر الزامی کنید، که به عنوان یک راز در هر دو طرف ذخیره شده باشد، و هر چیز دیگری را رد کنید.
۳. آن را idempotent کنید: اجرای دوبارهاش در همان دقیقه نباید دو یادآور بفرستد. آنچه ارسال شده را ثبت کنید.
۴. هر فراخوانی را کوتاه نگه دارید: یک دسته (مثلاً ۱۰۰ مورد) را پردازش کنید و بگذارید فراخوانی بعدی بقیه را بردارد.
۵. یک زمانبند را به آن وصل کنید.
انتخاب زمانبند:
- workflowهای زمانبندیشده GitHub Actions. در محدودیتهای استفاده GitHub رایگان است. طبق مستندات GitHub از سپتامبر ۲۰۲۶، کوتاهترین فاصله هر ۵ دقیقه یک بار است، اجراها ممکن است در ساعات شلوغ (بهویژه ابتدای هر ساعت) به تأخیر بیفتند، زمانبندیها به طور پیشفرض بر اساس UTC هستند، و workflowهای زمانبندیشده در یک مخزن عمومی بعد از ۶۰ روز بدون فعالیت مخزن غیرفعال میشوند.
- سرویسهای cron میزبانیشده. سرویسهای زیادی وجود دارند که تنها کارشان صدا زدن یک URL طبق برنامه است. پیش از وابستگی به یکی، قابلیت اطمینان، حداقل فاصله و هشدارها را بررسی کنید.
- زمانبند ارائهدهنده ابری شما. اگر از یک ارائهدهنده ابری بزرگ استفاده میکنید، زمانبند مدیریتشدهای دارد که میتواند یک endpoint HTTPS را صدا بزند.
- سرویس کار یا workflow میزبانیشده. اینها زمانبندی، صف و تلاش مجدد را با هم دارند و برای هر مرحله endpointهای شما را صدا میزنند. قیمت و محدودیتها را در سایت هر فروشنده مقایسه کنید.
الگوی ۳: بگذارید ارائهدهنده انجامش دهد
گاهی بهترین کار پسزمینه این است که اصلاً کاری نکنید:
- پرداختها. Stripe پرداختهای ناموفق اشتراک را دوباره امتحان میکند و وبهوک میفرستد؛ شما واکنش نشان میدهید به جای سرکشی. وبهوک چیست را ببینید.
- ایمیل. ارائهدهندگان ایمیل وقتی پیام را تحویل گرفتند، تحویل را صفبندی و دوباره امتحان میکنند.
- کار انبوه هوش مصنوعی. OpenAI و Anthropic APIهای دستهای برای درخواستهای غیرفوری ارائه میدهند که شما ارسال و بعداً جمعآوری میکنید.
دستوراتی برای ساز اپلیکیشن هوش مصنوعیتان
برای یک صف:
یک صف کار پسزمینه با یک جدول jobs در پایگاه دادهمان اضافه کن (نوع، payload به صورت JSON، وضعیت، تعداد تلاشها، run_after، آخرین خطا). وقتی کاربری ثبتنام میکند، یک کار send_welcome_email را در همان تراکنش وارد کن، به جای اینکه ایمیل را مستقیم بفرستی. یک تابع processJobs(limit) اضافه کن که کارهای سررسیده را با SELECT ... FOR UPDATE SKIP LOCKED برمیدارد، اجرایشان میکند، و شکستها را بعد از ۱، ۵ و ۳۰ دقیقه دوباره امتحان میکند و بعد از ۴ تلاش آنها را failed علامت میزند.
برای یک راهانداز زمانبندیشده:
POST /api/cron/tick را اضافه کن که با یک توکن bearer از راز CRON_SECRET محافظت شده باشد؛ هر درخواستی بدون آن را رد کن. هر فراخوانی processJobs(100) را اجرا کند و یادآورهای رزرو را که در ۲۴ ساعت آینده سررسید دارند و هنوز ارسال نشدهاند بفرستد، و یادآورهای ارسالشده را ثبت کند تا هیچ چیزی دوبار فرستاده نشود. یک خلاصه JSON از آنچه اجرا شد برگردان. سپس یک workflow برای GitHub Actions بنویس که هر ۱۵ دقیقه این endpoint را صدا بزند و URL و توکن را از secretهای مخزن بخواند.
اشتباههای رایج
- تکیه بر
setInterval. وقتی سرور میخوابد یا ریاستارت میشود بیسروصدا متوقف میشود، و وقتی دو نسخه اجرا شوند دوبرابر میشود. - endpoint بدون محافظت cron. هر کسی که آن را پیدا کند میتواند کارهایتان را شلیک کند، یا صورتحساب ایمیلتان را بالا ببرد.
- کارهایی که idempotent نیستند. تلاشهای مجدد و فراخوانیهای تکراری زمانبند پیش میآیند. برای آنها طراحی کنید.
- نبود دید نسبت به شکست. کاری که یک هفته بیسروصدا شکست بخورد بدتر از کاری است که با صدای بلند شکست بخورد. خطاها را ثبت کنید و برای شکستهای مکرر هشدار بدهید.
- اجراهای طولانی در یک درخواست. درخواستها timeout میشوند. دستهای پردازش کنید.
- زمانبندی خیلی زیاد. هر فراخوانی روی میزبانی که تا صفر مقیاس میشود اپ را بیدار و هزینهاش را بالا میبرد. فاصله را با نیاز واقعی هماهنگ کنید.
- منطقه زمانی. «۹ صبح» برای زمانبند شما (که اغلب UTC است) و برای کاربرانتان معانی متفاوتی دارد. زمانها را به UTC ذخیره کنید و برای هر کاربر تبدیل کنید.
- اسرار داخل فایل workflow. توکنها را در فروشگاه اسرار زمانبند نگه دارید. چطور کلیدهای API را امن نگه داریم را ببینید.
چکلیست
- کار کند از درخواستهای کاربر بیرون منتقل شده است
- کارها به شکل ماندگار ذخیره میشوند، نه فقط در حافظه
- تلاش مجدد با فاصلههای افزایشی و حداکثر تعداد
- هر کار اجرای دوبارهاش بیخطر است
- endpoint زمانبندیشده با یک توکن مخفی محافظت شده است
- هر اجرا یک دسته محدود را پردازش میکند
- کارهای شکستخورده قابلمشاهدهاند و هشدار دارند
- فاصله زمانبندی با توجه به هزینه میزبانی انتخاب شده است
- زمانها به UTC ذخیره میشوند
کارهای پسزمینه در Mythex
مستندات Mythex کارهای زمانبندیشده یا cron را توضیح نمیدهد، و اپهای منتشرشده و پایگاه دادههایشان وقتی بیکارند میخوابند و با درخواست بعدی بیدار میشوند (ببینید محدودیتهای میزبانی). پس به تایمرهای داخل اپ تکیه نکنید. روشی که کار میکند همان است که بالا گفته شد: از عامل یک جدول jobs و یک endpoint محافظتشده با توکن بخواهید، سپس یک زمانبند بیرونی را به URL منتشرشدهتان وصل کنید. توکن را به عنوان راز پروژه ذخیره کنید، و به یاد داشته باشید که هر بیدارشدن برای مدت زمانی که اپ و پایگاه داده بیدارند اعتبار مصرف میکند، پس یک فاصله معقول انتخاب کنید. اگر اپ شما برای این کار به بکاند API نیاز دارد، دستور REST API backend نقطه شروع است، و چطور یک REST API بسازیم طراحی endpointها را پوشش میدهد. اگر رباتها یا workerهای همیشهروشن هسته محصول شما هستند، میزبانی که برای فرایندهای پایدار ساخته شده شاید مناسبتر باشد.
پرسشها
کار پسزمینه چیست؟
کار پسزمینه کاری است که اپ شما بیرون از درخواستی انجام میدهد که کاربر منتظرش است، مثل ارسال ایمیل، تغییر اندازه تصاویر، ساختن گزارش یا همگامسازی داده. کاربر پاسخ سریع میگیرد، کار جداگانه اجرا میشود و اگر شکست بخورد میتوان دوباره امتحانش کرد.
اگر میزبانم cron ندارد، چطور یک کار زمانبندیشده اجرا کنم؟
یک endpoint محافظتشده اضافه کنید که کار را انجام میدهد، سپس یک زمانبند بیرونی را طوری تنظیم کنید که آن را طبق یک برنامه صدا بزند. گزینهها شامل یک workflow زمانبندیشده در GitHub Actions، یک سرویس cron میزبانیشده یا زمانبند ارائهدهنده ابری شماست. endpoint را با یک توکن مخفی محافظت کنید.
آیا میتوانم برای کارهای زمانبندیشده از setInterval یا setTimeout استفاده کنم؟
فقط برای کارهای کوچک و غیرمهم روی سروری که همیشه در حال اجراست. اگر سرور ریاستارت شود، تا صفر مقیاس شود یا چند نسخه از آن اجرا شود، تایمرها از بین میروند یا بیش از یک بار اجرا میشوند. کار زمانبندیشده را در پایگاه داده ذخیره کنید یا از یک زمانبند استفاده کنید.
آیا Mythex از کارهای cron پشتیبانی میکند؟
مستندات Mythex کارهای زمانبندیشده یا cron را توضیح نمیدهد، و اپهای منتشرشده وقتی بیکارند میخوابند. روش قابل اعتماد این است که یک زمانبند بیرونی به یک endpoint محافظتشده در اپ شما وصل شود؛ آن اپ را بیدار میکند و کار را اجرا میکند.