با استفاده از صف‌های لاراول محصول خود را برای مقایسه آماده کنید

ترجمه و تالیف : عرفان حشمتی
تاریخ انتشار : 13 مهر 99
خواندن در 5 دقیقه
دسته بندی ها : لاراول

این آموزش برای کلیه برنامه نویسان پی اچ پی است که یک برنامه آنلاین با کاربران واقعی دارند، اما آن‌ها نیاز به درک عمیق‌تری از نحوه معرفی (یا بهبود چشمگیر) مقیاس‌پذیری در سیستم خود با استفاده از صف‌های لاراول دارند.

اولین باری که در مورد لاراول خواندم، اواخر سال 2013 در ابتدای نسخه 5 فریمورک بود. من هنوز یک توسعه دهنده در پروژه‌های مهم نبودم و یکی از جنبه‌های فریمورک‌های مدرن، به خصوص در لاراول که به نظرم اسرارآمیزترین برای من "صف" بود.

امروز من خالق Inspector.dev داشبورد نظارت در بی درنگ هستم که هزاران شغل را در هر ساعت مدیریت می‌کند. بنابراین دانش من در مورد این معماری بسیار بهتر از گذشته است.

در این مقاله قصد دارم به شما نشان دهم که چگونه queue ها و job ‌ها را کشف کردم و چه تنظیماتی به من کمک کرد تا ضمن حفظ منابع سرور که مقرون به صرفه باشد، مقدار زیادی از داده‌ها را در زمان واقعی نگهداری کنم.

مقدمه

هنگامی که یک برنامه پی اچ پی یک درخواست http ورودی دریافت می‌کند، کد ما به صورت متوالی مرحله به مرحله اجرا می‌شود تا زمان اجرای درخواست به پایان برسد و پاسخی به کاربر برگردانده شود (به عنوان مثال، مرورگر کاربر).

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

queue ها و job ها رفتارهای ناهمزمان را ارائه می‌دهند که این جریان خطی را شکسته است. به همین دلیل فکر می‌کنم در ابتدا این کارکردها برای من کمی عجیب به نظر می‌رسید.

اما گاهی اوقات یک کار وقت‌گیر شامل اتمام چرخه اجرا به دلیل یک درخواست http دریافتی، به عنوان مثال ارسال یک اعلان نامه الکترونیکی به اعضای تیم یک پروژه است.

این به معنای ارسال شش یا ده ایمیل است و ممکن است چهار یا پنج ثانیه طول بکشد. بنابراین هر بار که کاربر روی آن دکمه کلیک کند، باید پنج ثانیه صبر کند تا بتواند به استفاده از برنامه ادامه دهد.

هرچه برنامه بیشتر شود، این مشکل بیشتر می‌شود.

Job چیست؟

Job یک کلاس است که متد "handle" را اجرا می‌کند که شامل منطقی است که می‌خواهیم بصورت ناهمزمان اجرا کنیم.

<?php

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Bus\Queueable;


class CallExternalAPI implements ShouldQueue
{
    use Dispatchable,
        InteractsWithQueue,
        Queueable;
        
    /**
     * @var string
     */
    protected $url;

    /**
     * Create a new job instance.
     *
     * @param array $Data
     */
    public function __construct($url)
    {
        $this->url = $url;
    }
    

    /**
     * Execute what you want.
     *
     * @return void
     * @throws \Throwable
     */
    public function handle()
    {
        file_get_contents($this->url);
    }
}

همانطور که در بالا ذکر شد دلیل اصلی کپسوله کردن یک تکه کد در یک job، انجام یک کار وقت‌گیر بدون مجبور کردن کاربر به انتظار اجرای آن است.

منظور از "کارهای وقت‌گیر" چیست؟

این یک سوال متداول است. ارسال ایمیل رایج‌ترین نمونه‌ای است که در مقاله‌هایی استفاده می‌شود که در مورد queue ها صحبت می‌کنند، اما من می‌خواهم به شما بگویم که در تجربه واقعی چه کاری را باید انجام دهید.

به عنوان صاحب محصول، این مهم است که اطلاعات کاربران را با ابزارهای بازاریابی و پشتیبانی مشتری همگام کنیم. بنابراین بر اساس اقدامات کاربر، ما اطلاعات را به نرم‌افزارهای مختلف خارجی از طریق API برای اهداف بازاریابی و مراقبت از مشتری بروزرسانی می‌کنیم.

یکی از مهم‌ترین موارد استفاده شده در برنامه ما این است که می‌تواند 10 ایمیل را ارسال کند و 3 تماس http را برای انجام خدمات خارجی انجام دهد. هیچ کاربر منتظر این همه مدت نیست، به احتمال زیاد آن‌ها از استفاده از این برنامه منصرف می‌شوند.

با تشکر از queue، می‌توانم تمام این کارها را در کلاس‌های اختصاصی جمع‌بندی کنم، اطلاعات مربوط به انجام کار خود را در مخزن منتقل کنم و زمان اجرای آن‌ها را برای پس‌زمینه تنظیم کنم تا کنترل کننده من بتواند بلافاصله پاسخی را برگرداند.

<?php

class ProjectController 
{
    public function store(Request $request)
    {
        $project = Project::create($request->all());
        
        // Defer NotifyMembers, TagUserActive, NotifyToProveSource 
        // passing the information needed to do their job
        Notification::queue(new NotifyMembers($project->owners));
        $this->dispatch(new TagUserAsActive($project->owners));
        $this->dispatch(new NotifyToProveSource($project->owners));
        
        return $project;
    }
}

لازم نیست صبر کنید تا تمام این مراحل قبل از بازگشت پاسخ تمام شود. بلکه فقط به زمان لازم برای انتشار آن‌ها در queue منتظر خواهیم ماند. این می‌تواند به معنای تفاوت بین 10 ثانیه و 10 میلی ثانیه باشد!

چه کسی این کارها را بعد از ارسال در صف انجام می‌دهد؟

این یک معماری کلاسیک "تولید کننده / مصرف کننده" است. ما به تازگی job خود را در queue از کنترلر منتشر کرده‌ایم، بنابراین اکنون قصد داریم نحوه مصرف queue و در نهایت job های اجرا شده را بفهمیم.

برای مصرف یک queue، باید یکی از محبوب‌ترین فرمان‌های artisan را اجرا کنیم:

php artisan queue:work

لاراول شامل یک کارگر صف است که به محض فشار بر روی صف، وظایف جدید را پردازش می‌کند.

همچنین لاراول یک رابط کاربری آماده برای قرار دادن وظایف در یک صف و یک دستورالعمل آماده استفاده برای بیرون کشیدن وظایف از صف و اجرای کد آنها در پس زمینه فراهم می‌کند.

نقش supervisor

در ابتدا این چیز عجیبی بود. فکر می‌کنم کشف چیزهای جدید طبیعی است. همچنین من این مرحله از مطالعه را تجربه کرده‌ام، بنابراین این مقاله‌ها را می‌نویسم تا به من در سازماندهی مهارت‌های خود کمک کند و در عین حال می‌توانم به سایر توسعه دهندگان نیز کمک کنم تا دانش خود را گسترش دهند.

اگر یک فرایند حین اجرای استثنا شکست بخورد، صف دستور اجرای کار خود را متوقف می‌کند.

برای حفظ صف روند کار به طور دائم در حال اجرا است. باید از یک نظارت‌گر فرآیند مانند Supervisor استفاده کنید تا اطمینان حاصل شود که فرمان queue: work حتی اگر یک استثنا را از بین ببرد، متوقف نمی‌شود.

سوپروایزر بعد از از کار افتادن فرمان، ریستارت می‌شود و کار بعدی را شروع می‌کند و فرایند دیگری را که شکست خورده است، رها می‌کند.

کارها به صورت پس زمینه بر روی سرور شما اجرا می‌شوند، دیگر به درخواست HTTP بستگی ندارد. این تغییراتی را ارائه می‌دهد که من هنگام اجرای کد job باید در نظر داشته باشم.

در صورت عدم موفقیت کد job، چگونه می‌توان باخبر شد؟

در حال اجرا در پس زمینه، اگر فرایند شما خطایی ایجاد کند، نمی‌توانید فورا آن را ببینید.

دیگر بازخورد فوری مانند اجرای درخواست http از مرورگر خود ندارید. در صورت شکست خوردن یک فرایند، این کار را مخفیانه انجام خواهد داد، بدون اینکه کسی متوجه شود.

در نظر بگیرید که یک ابزار نظارت بی‌درنگ مانند سوپروایزر را یکپارچه کنید تا هر اشکال را بررسی کند.

شما درخواست http را ندارید

درخواست http از بین رفته است. کد شما از cli اجرا خواهد شد.

اگر برای انجام وظایف خود به پارامترهای درخواستی نیاز دارید، باید آن‌ها را در کانستراکتور job منتقل کنید تا بعداً بتوانید از آن‌ها استفاده کنید:

<?php

// A job class example
class TagUserJob implements ShouldQueue
{
    public $data;
    
    public function __construct(array $data)
    {
        $this->data = $data;
    }
}

// Put the job in the queue from your controller
$this->dispatch(new TagUserJob($request->all()));

شما نمی‌دانید کاربر وارد شده چه کسی است

سشن از بین رفته است. به همین روش شما هویت کاربری را که وارد سیستم شده نمی‌دانید، بنابراین اگر برای انجام کار به اطلاعات کاربر نیاز دارید، باید شیء کاربر را به کانستراکتور منتقل کنید:

<?php

// A job class example
class TagUserJob implements ShouldQueue
{
    public $user;
    
    public function __construct(User $user)
    {
        $this->user= $user;
    }
}

// Put the job in the queue from your controller
$this->dispatch(new TagUserJob($request->user()));

درک نحوه مقیاس‌پذیری

متأسفانه در بسیاری از موارد کافی نیست. با استفاده از یک صف واحد و مصرف کننده ممکن است به زودی بی‌فایده شود.

صف‌ها بافر FIFO هستند (First In First Out). یعنی اولین فرایندی که وارد شده زودتر از بقیه خارج می‌شود.

دو روش برای مقیاس وجود دارد:

مصرف کنندگان چندگانه برای یک صف

 

به این ترتیب، پنج کار به طور هم زمان از صف کشیده می‌شوند و باعث افزایش مصرف صف می‌شوند.

صف‌های تک منظوره

همچنین می‌توانید برای هر نوع job که راه‌اندازی می‌کنید، صف‌های خاصی ایجاد کنید همراه با یک مصرف کننده اختصاصی برای هر صف.

به این ترتیب هر صف بدون نیاز به انتظار برای اجرای سایر کارها، به طور مستقل مصرف می‌شود.

پیش به سمت Horizon

Laravel Horizon یک مدیر صف است که به شما امکان کنترل کامل تعداد صف‌هایی را که می‌خواهید تنظیم کنید و توانایی سازماندهی مصرف کنندگان را می‌دهد. به توسعه دهندگان اجازه می‌دهد این دو استراتژی را در کنار هم داشته باشند و یکی را پیاده‌سازی کنند که متناسب با نیازهای مقیاس پذیریشان باشد.

همه این کارها با اجرای php artisan horizon به جای php artisan queue:work آغاز می‌شود. این دستور فایل پیکربندی horizon.php شما را اسکن می‌کند و تعدادی کارگر صف را بر اساس پیکربندی ایجاد می‌کند:

<?php

'production' => [
    'supervisor-1' => [
        'connection' => "redis",
        'queue' => ['adveritisement', 'logs', 'phones'],
        'processes' => 9,
        'tries' => 3,
        'balance' => 'simple', // could be simple, auto, or null
    ]
]

در مثال بالا، Horizon سه صف با سه فرآیند اختصاص داده شده برای مصرف هر صف را آغاز می‌کند.

همانطور که در اسناد لاراول ذکر شد رویکرد کد محور Horizon به پیکربندی من اجازه می‌دهد تا در کنترل منبع جایی داشته باشد که تیم من بتواند همکاری کند. این یک راه‌حل عالی با استفاده از یک ابزار CI است.

پیکربندی خودم

<?php

'production' => [
    'supervisor-1' => [
        'connection' => 'redis',
        'queue' => ['default', 'ingest', 'notifications'],
        'balance' => 'auto',
        'processes' => 15,
        'tries' => 3,
    ],
]

سوپروایزر عمدتا از سه صف استفاده می‌کند:

  • ingest برای پردازش داده‌ها به منظور تجزیه و تحلیل داده‌های برنامه‌های خارجی است.
  • در صورت مشاهده خطا در هنگام ingest داده‌ها، از نوتیفیکیشن‌ها برای برنامه‌ریزی اعلان‌ها استفاده می‌شود.
  • به طور پیش فرض برای کارهای دیگری استفاده می‌شود که نمی‌خواهم در ingest فرآیندهای ورودی و نوتیفیکیشن‌ها دخالت کنم.

با استفاده از تعادل خودکار Horizon می‌داند که حداکثر تعداد فرآیندهای فعال شده 15 عدد است که با توجه به بار صف‌ها به صورت پویا توزیع می‌شود.

اگر صف‌ها خالی باشند، Horizon یک فرایند را برای هر صف فعال نگه می‌دارد و در صورت برنامه‌ریزی، یک مصرف کننده را آماده پردازش صف می‌کند.

سخن پایانی

اجرای همزمان پس زمینه می‌تواند باعث شود بسیاری از اشکالات غیرقابل پیش‌بینی دیگر مانند MySQL " قفل مدت زمان انتظار بیش ازحد" و بسیاری دیگر از مشکلات طراحی برطرف شوند.

امیدوارم این مقاله به شما کمک کند تا با اطمینان بیشتری از queue ها و job ها استفاده کنید.

منبع

گردآوری و تالیف عرفان حشمتی
آفلاین
user-avatar

عرفان حشمتی هستم، مهندس سخت افزار و برنامه نویس و طراح وب سایت، علاقه مند به دنیای آی تی و تکنولوژی، همچنین در حوزه ادیت فیلم و تصویر مطالعه و تمرین می کنم.

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید