<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>نوشته های Mohammad Hosein Abedi</title><link>https://virgool.ir/feed/@mhs.abedi</link><description>دولوپر @ Bamilo</description><language>fa</language><pubDate>2026-11-11 16:28:16</pubDate><image><url>https://files.virgool.ir/upload/users/1593/avatar/FgF5CS.png</url><title>Mohammad Hosein Abedi</title><link>https://virgool.ir/@mhs.abedi</link></image><item><title>زنجیره جاب‌ها!</title><link>https://virgool.ir/@mhs.abedi/زنجیره-جابها-sctwm9zjfwxd</link><description>چند روز از اختتامیه جشنواره وب و موبایل ایران می‌گذره و چیزی که خیلی در مورد سخنرانی‌ها به گوشم می‌خورد این بود که چرا فلانی تجربه‌های واقعی تیم خودشون رو بیان نکرد و چرا بحث‌های تئوری که توی کتاب‌ها و مقالات می‌شه پیدا کرد رو توضیح می‌داد و به مثال‌های واقعی نپرداخت. به این فکر افتادم که یکی از مشکلاتی که اخیرا بهش برخوردیم و به راه حل مناسبی براش رسیدیم رو به اشتراک بگذارم.طی چند‌ هفته‌ی گذشته یک پروژه جدید و جذاب لانچ کردیم که بعلت ذات پروژه مجبور بودیم قسمت‌های فراوانی از کار‌های اون رو به صورت Job های مستقل تقسیم کنیم و اونها رو Queue کنیم که در پس زمینه اجرا شوند. پروژه ما با زبان PHP و در چارچوب Laravel 5.5 توسعه داده شده بود، برای Queue از ‌Redis و برای مانیتور کردن Queue ها از Horizon استفاده می‌کردیم . یکی از قسمت‌های این پروژه باید با API یکی از بانک‌ها کار‌ می‌کرد. در این سیستم یک‌سری تراکنش تعریف شده بود که ما تمام یا قسمتی از آن تراکنش را برگشت می‌زدیم(یکجا یا در چند مرحله). API بانک مورد نظر دو مرحله‌ای بود به صورتی که در مرحله اول شماره تراکنش و مبلغی از تراکنش که قرار بود بازگشت بخورد را به بانک اعلام می‌کردیم و در مرحله دوم عملیات مرحله اول را تایید می‌کردیم. بانک در صورتی که عملیات مورد نظر ما صحیح بود (شماره تراکنش صحیح بود و مبلغ برگشت خورده از جمع مبلغ تراکنش کم‌تر بود) به ما پیغام عملیات موفق و در غیر این صورت خطا می‌داد.در مرحله اول ما تمام تراکنش‌هایی که قرار بود برگشت بخورند را Job در نظر گرفتیم(تراکنش‌ها به صورت دسته‌ای وارد سیستم می‌شدند) و این Job ها را در پس‌زمینه پردازش می‌کردیم. طبق تست‌های اولیه همه چیز صحیح و درست بود.اما مشکلی که پیش آمد چه بود؟تراکنش‌هایی که به صورت دسته‌ای می‌آمدند ممکن است به یک تراکنش تعلق داشته باشند ( مثلا در سیستم، برای تراکنش ۱۰۰ هزار تومانی A یک تراکنش برگشت ۵۰ هزار تومانی و یک تراکنش برگشت ۲۰ هزار تومانی ثبت شده بود. به‌ خاطر ذات تراکنش‌ها امکان تجمیع آنها نبود مثلا در این حالت نمی‌توانستیم یک تراکنش ۷۰ هزار تومانی برگشت بزنیم) ما تراکنش ۵۰ هزارتومانی و تراکنش ۲۰ هزارتومانی را به صورت جدا Queue می‌کردیم و جداگانه به بانک اعلام می‌کردیم که آن‌ها را برگشت بزند. حالتی را فرض کنید که تراکنش ۲۰ هزارتومانی اولین مرحله از فراخوانی API را انجام دهد (از دو مرحله API بانک) و قبل از اینکه این تراکنش API تایید را فراخوانی کند نوبت به اجرای تراکنش ۵۰ هزاتومانی شود و این تراکنش اولین مرحله از API را فراخوانی کند در این حالت بانک به ما پیغام تراکنش جاری تراکنشی با وضعیت معلق دارد را می‌داد به این معنی که هنوز تکلیف تراکنش ۵۰ هزارتومانی را مشخص نکرده بودیم. (یعنی آن‌را تایید نکرده بودیم) در واقع انگار ما باید کاری می‌کردیم که تراکنش‌های برگشت مربوط به یک تراکنش به صورت متوالی انجام شوند (یکی پس از پایان یکی دیگر) حال با توجه به ذات سیستم که از Job های مختلفی تشکیل شده بود باید چه می‌کردیم؟ راه حل‌های زیر قابل تصور بودذات سیستم را تغییر دهیم که به صورت Cron Job عمل کند به طوری که در بازه‌های زمانی خاص به درخواست‌های بازگشتی که آمده بود سر می‌زدیم و آنها را یکی پس از دیگری پردازش می‌کردیم بدون استفاده از Queue،  مشکلی که این روش برای ما داشت تاخیری بود که برای اجرا شدن تراکنش‌ها صورت می‌گرفت به این حالت که ما مجبور بودیم Cron Job را ۳۰ دقیقه یک بار اجرا کنیم و این تاخیر قابل قبول نبود به اضافه این‌که کل سیستم به صورت Job های مستقل بود و یک تکه از آن به صورت Cron Job در می‌آمد که این هم برای ما خوشایند نبود.هنگام Consume کردن Queue به نحوی عمل کنیم که در هر لحظه فقط یک Job پردازش شود که این باعث کندی شدید سیستم می‌شد چرا که اکثر تراکنش‌های بازگشت سیستم ما به صورت تکی بودند مثلا کل تراکنش ۱۰۰ هزارتومانی برگشت بخورد و در این راه حل ما باید برای حدود ۲۰ درصد Job ها کل سیستم را کند می‌کردیم. در حال حاضر ما از ویژگی balance option در horizon استفاده می‌کنیم که به صورت خودکار به Queue هایی که لود بالایی دارند Consumer های بیشتری اختصاص می‌دهد که با این روش امکان استفاده از balance در horizon هم برای ما فراهم نبود.اما راه حلی که ما پیش گرفتیم:برگرفته از داکیومنت رسمی لاراولاستفاده از Job Chaining بود به این صورت که ابتدا درخواست‌های دسته‌ای که برای بازگشت به سیستم وارد می‌شد را بر اساس شماره تراکنش گروه بندی می‌کردیم و هر گروه را به صورت Job Chaining اجرا می‌کردیم (برای هر گروه یک آرایه از Job ها داشتیم که به صورت زنجیر وار اجرا می شد). در این حالت در یک زنجیره زمانی که یک Job به صورت کامل تمام شود Job بعدی زنجیره اجرا می‌شود.نکته بسیار ریزی که وجود دارد این است که چگونه ابتدای زنجیره را مشخص کنیم (در عکس بالا ProcessPodcast) راه حل‌های متعددی برای این کار وجود دارد مثلا استفاده از Reflection بر روی اولین عنصر از آرایه Job هایی که قرار است Chain شوند اما ما راه بسیار ساده‌ای پیش گرفتیم استفاده از یک Job خالی مثلا با نام JobDispatcher یعنی ابتدای زنجیره ما یک Job با زمان اجرای صفر است! در آخر کد ما چیزی شبیه به کد زیر شد.JobDispatcher::withChain($jobGroupByTransaction)-&gt;dispatch();</description><category>Mohammad Hosein Abedi</category><author>Mohammad Hosein Abedi</author><pubDate>Sun, 18 Feb 2018 17:56:09 +0330</pubDate></item></channel></rss>