جعل منتجنا جاهزًا للتوسع مع قوائم انتظار Laravel

تم إعداد ترجمة للمقال خصيصًا لطلاب دورة "Laravel" .




مرحبًا ، أنا فاليريو ، مهندس برمجيات من إيطاليا.

هذا الدليل مخصص لجميع مطوري PHP الذين لديهم بالفعل تطبيقات عبر الإنترنت مع مستخدمين حقيقيين ، لكنهم يفتقرون إلى فهم أعمق لكيفية تنفيذ (أو تحسين بشكل كبير) قابلية التوسع في نظامهم باستخدام قوائم انتظار Laravel. لقد علمت لأول مرة عن Laravel في نهاية عام 2013 في بداية الإصدار الخامس من الإطار. ثم لم أكن بعد مطورًا مشاركًا في مشاريع جادة ، وكانت قوائم الانتظار أحد جوانب الأطر الحديثة ، خاصة في Laravel ، التي بدت بالنسبة لي الأكثر غير مفهومة.

عند قراءة الوثائق ، خمنت حول الإمكانات ، ولكن بدون خبرة تطوير حقيقية ، بقي كل هذا فقط على مستوى النظريات في رأسي.
اليوم، أنا مبدع Inspector.dev- لوحة أجهزة القياس الحقيقي ، الذي ينفذ آلاف من المهام في كل ساعة، وأنا أفهم هذه العمارة أفضل بكثير من ذي قبل.



في هذه المقالة سأخبرك كيف اكتشفت قوائم الانتظار والمهام وما هي التكوينات التي ساعدتني في معالجة كميات كبيرة من البيانات في الوقت الفعلي ، مع توفير موارد الخادم.

المقدمة


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

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

وكلما زاد التطبيق ، أصبحت هذه المشكلة أسوأ.

ما هي المهمة؟


المهمة هي فئة تطبق أسلوب المقبض الذي يحتوي على المنطق الذي نريد تنفيذه بشكل غير متزامن.

<?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;

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

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

كما ذكر أعلاه ، فإن السبب الرئيسي لإبرام جزء من التعليمات البرمجية في Job هو إكمال مهمة تستغرق وقتًا طويلاً ، دون إجبار المستخدم على الانتظار حتى يكتمل.

ماذا نعني بـ "المهام الشاقة"؟


هذا سؤال طبيعي. يعد إرسال رسائل البريد الإلكتروني هو المثال الأكثر شيوعًا المستخدم في المقالات التي تتحدث عن قوائم الانتظار ، ولكن أريد أن أخبرك عن التجربة الحقيقية لما كنت بحاجة إلى القيام به.

بصفتي مالكًا للمنتج ، من المهم جدًا بالنسبة لي مزامنة معلومات سفر المستخدم مع أدوات التسويق ودعم العملاء. وبالتالي ، بناءً على إجراءات المستخدم ، نقوم بتحديث معلومات المستخدم في برامج خارجية مختلفة عبر واجهة برمجة التطبيقات (أو مكالمات http الخارجية) لأغراض الخدمة والتسويق.
يمكن لإحدى نقاط النهاية الأكثر استخدامًا في طلبي إرسال 10 رسائل بريد إلكتروني وإجراء 3 مكالمات http إلى الخدمات الخارجية قبل الانتهاء. لن ينتظر أي مستخدم الكثير من الوقت - على الأرجح ، كلهم ​​يتوقفون ببساطة عن استخدام تطبيقي.

بفضل قوائم الانتظار ، يمكنني تغليف جميع هذه المهام في الفئات المخصصة ، ونقل المعلومات اللازمة لتنفيذ عملهم إلى المنشئ ، وجدولة إتمامها في وقت لاحق في الخلفية حتى يتمكن جهاز التحكم من إرجاع الإجابة على الفور.

<?php

class ProjectController 
{
    public function store(Request $request)
    {
        $project = Project::create($request->all());
        
        //  NotifyMembers, TagUserActive, NotifyToProveSource 
        //   ,     
        Notification::queue(new NotifyMembers($project->owners));
        $this->dispatch(new TagUserAsActive($project->owners));
        $this->dispatch(new NotifyToProveSource($project->owners));
        
        return $project;
    }
}

لست بحاجة إلى الانتظار حتى اكتمال كل هذه العمليات قبل إعادة الجواب ؛ على العكس من ذلك ، سيكون الانتظار مساوياً للوقت المطلوب لنشره في قائمة الانتظار. وهذا يعني الفرق بين 10 ثوان و 10 مللي ثانية !!!

من يقوم بهذه المهام بعد إرسالها إلى قائمة الانتظار؟


هذه هي هندسة الناشر / المستهلك الكلاسيكية . لقد نشرنا بالفعل مهامنا في قائمة الانتظار من وحدة التحكم ، والآن سنفهم كيفية استخدام قائمة الانتظار ، وأخيرًا ، يتم تنفيذ المهام.



لاستخدام قائمة الانتظار ، نحتاج إلى تشغيل أحد أوامر الحرفيين الأكثر شيوعًا:

php artisan queue:work

كما تقول الوثائق:
يشتمل Laravel على عامل في قائمة الانتظار يقوم بمعالجة المهام الجديدة عندما يتم وضعها في قائمة الانتظار.

عظيم! يوفر Laravel واجهة جاهزة لمهام الانتظار وأمر جاهز لاسترداد المهام من قائمة الانتظار وتنفيذ التعليمات البرمجية الخاصة بهم في الخلفية.

دور المشرف


لقد كان "شيء غريب" آخر في البداية. أعتقد أنه من الطبيعي اكتشاف أشياء جديدة. بعد هذه المرحلة من التدريب ، أكتب هذه المقالات لمساعدة نفسي على تنظيم مهاراتي ، وفي الوقت نفسه مساعدة المطورين الآخرين على توسيع معرفتهم.

إذا فشلت المهمة أثناء إجراء استثناء ، سيتوقف الفريق queue:workعن عمله.

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

سيتم تشغيل المهام في الخلفية على خادمك ، ولم تعد تعتمد على طلب HTTP. يقدم هذا بعض التغييرات التي كان عليّ مراعاتها عند تنفيذ رمز المهمة.

فيما يلي أهمها التي سأولي اهتمامًا لها:

كيف يمكنني معرفة أن رمز المهمة لا يعمل؟


عند التشغيل في الخلفية ، لا يمكنك أن تلاحظ على الفور أن مهمتك تسبب أخطاء.
لن يكون لديك تعليقات فورية بعد الآن ، مثل عند تقديم طلب http من متصفحك. إذا فشلت المهمة ، فستفعلها بصمت ولن يلاحظها أحد.

ضع في اعتبارك دمج أداة مراقبة في الوقت الفعلي مثل المفتش في إظهار كل العيوب.

ليس لديك طلب http


لا يوجد المزيد من طلب HTTP. سيتم تنفيذ التعليمات البرمجية الخاصة بك من cli.

إذا كنت بحاجة إلى معلمات استعلام لأداء مهامك ، فأنت بحاجة إلى تمريرها إلى مُنشئ المهام لاستخدامها لاحقًا في وقت التشغيل:

<?php

//   

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

//       

$this->dispatch(new TagUserJob($request->all()));

أنت لا تعرف من قام بتسجيل الدخول


لم تعد هناك جلسة . بنفس الطريقة ، لن تعرف هوية المستخدم الذي قام بتسجيل الدخول ، لذلك إذا كنت بحاجة إلى معلومات حول المستخدم لإكمال المهمة ، فأنت بحاجة إلى تمرير كائن المستخدم إلى مُنشئ المهمة:

<?php

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

//       
$this->dispatch(new TagUserJob($request->user()));

افهم كيفية القياس


لسوء الحظ ، في كثير من الحالات هذا لا يكفي. قد يصبح استخدام سطر واحد والمستهلك عديم الفائدة قريبًا.

قوائم الانتظار هي مخازن FIFO المؤقتة ("أولاً في ، أولاً يخرج"). إذا كنت قد خططت للعديد من المهام ، وربما حتى من أنواع مختلفة ، فعليهم الانتظار حتى يكمل الآخرون مهامهم قبل إكمالها.

هناك طريقتان للتوسع:

العديد من المستهلكين لكل قائمة انتظار



، وبالتالي ، ستتم إزالة خمس مهام من قائمة الانتظار في كل مرة ، مما يسرع من معالجة قائمة الانتظار.

قوائم الانتظار ذات الأغراض الخاصة

يمكنك أيضًا إنشاء قوائم انتظار محددة لكل نوع من المهام ليتم إطلاقها مع عميل مخصص لكل قائمة انتظار.



وبالتالي ، سيتم معالجة كل قائمة انتظار بشكل مستقل ، دون انتظار اكتمال أنواع أخرى من المهام.

الأفق


Laravel Horizon هو مدير قائمة انتظار يمنحك تحكمًا كاملاً في عدد قوائم الانتظار التي تريد إعدادها ، والقدرة على تنظيم المستهلكين ، مما يسمح للمطورين بدمج هاتين الإستراتيجيتين وتنفيذ الاستراتيجية التي تناسب احتياجات قابلية التوسع الخاصة بك.

يحدث الإطلاق من خلال أفق الحرفيين php بدلاً من قائمة انتظار الحرفيين php: العمل . يقوم هذا الأمر بفحص ملف التكوين الخاص بك horizon.phpويبدأ سلسلة من عمال قائمة الانتظار بناءً على التكوين:

<?php

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

    ]
]

في المثال أعلاه ، ستبدأ Horizon ثلاث قوائم انتظار مع ثلاث عمليات مخصصة لمعالجة كل قائمة انتظار. كما هو مذكور في وثائق Laravel ، يسمح نهج Horizon الذي يعتمد على التعليمات البرمجية للبقاء في نظام التحكم في الإصدار حيث يمكن لفريقي التعاون. هذا هو الحل المثالي باستخدام أداة CI.

لمعرفة معنى معلمات التكوين بالتفصيل ، اقرأ هذه المقالة الرائعة .

التكوين الخاص بي



<?php

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

يستخدم المفتش بشكل رئيسي ثلاث قوائم انتظار:

  • استيعاب عمليات تحليل البيانات من التطبيقات الخارجية ؛
  • تُستخدم الإشعارات لجدولة الإشعارات على الفور إذا تم اكتشاف خطأ أثناء تلقي البيانات ؛
  • يستخدم الافتراضي للمهام الأخرى التي لا تريد خلط مع استيعاب و إخطارات العمليات.

من خلال استخدام balance=autoHorizon ، يدرك أن الحد الأقصى لعدد العمليات التي يتم تنشيطها هو 15 ، والتي سيتم توزيعها ديناميكيًا اعتمادًا على تحميل قوائم الانتظار.

إذا كانت قوائم الانتظار فارغة ، يدعم Horizon عملية نشطة واحدة لكل قائمة انتظار ، مما يسمح للمستهلك بمعالجة قائمة الانتظار على الفور إذا تمت جدولة مهمة.

الملاحظات الختامية


يمكن أن يؤدي تنفيذ الخلفية المتزامن إلى العديد من الأخطاء الأخرى التي لا يمكن التنبؤ بها ، مثل MySQL "Lock time out out" والعديد من مشاكل التصميم الأخرى. اقرأ المزيد هنا .

آمل أن تكون هذه المقالة قد ساعدت في استخدام قوائم الانتظار والمهام بمزيد من الثقة. إذا كنت تريد معرفة المزيد عنا ، فقم بزيارة موقعنا على www.inspector.dev

المنشور سابقًا هنا www.inspector.dev/what-worked-for-me-using-laravel-queues-from-the-basics-to -الأفق



احصل على الدورة واحصل على خصم.



All Articles