خطوة 18: الذهاب المتزامن

5.0 version
Maintained

الذهاب المتزامن

التحقق من البيانات التطفلية (Spam) أثناء معالجة الاستمارة (الفورم Form) قد يؤدي لبعض المشاكل. لو ان ال Akismet API اصبحت بطيئة، كذلك موقعنا سوف يصبح بطئ بالنسبة للمستخدمين. لكن أسوء من ذلك، اذا وصلنا إلي وقت مستقطع (timeout) أو ال Akismet API أصبحت غير متاحة، من الممكن ان نخسر التعليقات (comments).

بصورة مثالية، يجب علينا حفط البيانات المقدمة دون نشرها، وإرسال رد في الحال. والتحقق من البيانات التطفلية (spam) يمكن أن يتم خارج النطاق (out of band).

إبراز التعليقات

نحتاج إلي تقديم حالة state للتعليقات مثل: submitted`, spam, و published.

أضف خاصية (property) الحالة state` إلي فئة (كلاس) التعليقات Comment.

1
$ symfony console make:entity Comment

قم بإنشاء database migration:

1
$ symfony console make:migration

قم بتعديل ال migration لتحديث جميع التعليقات الموجودة لتصبح published بشكل تلقائي (default):

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
--- a/migrations/Version00000000000000.php
+++ b/migrations/Version00000000000000.php
@@ -20,7 +20,9 @@ final class Version20200714155905 extends AbstractMigration
     public function up(Schema $schema) : void
     {
         // this up() migration is auto-generated, please modify it to your needs
-        $this->addSql('ALTER TABLE comment ADD state VARCHAR(255) NOT NULL');
+        $this->addSql('ALTER TABLE comment ADD state VARCHAR(255)');
+        $this->addSql("UPDATE comment SET state='published'");
+        $this->addSql('ALTER TABLE comment ALTER COLUMN state SET NOT NULL');
     }

     public function down(Schema $schema) : void

قم بتحديث قاعدة البيانات (Migrate the database):

1
$ symfony console doctrine:migrations:migrate

وينبغي علينا أن نتأكد من أن الحالة state أصبحت submitted بشكل أساسي (افتراضي default):

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
--- a/src/Entity/Comment.php
+++ b/src/Entity/Comment.php
@@ -49,9 +49,9 @@ class Comment
     private $photoFilename;

     /**
-     * @ORM\Column(type="string", length=255)
+     * @ORM\Column(type="string", length=255, options={"default": "submitted"})
      */
-    private $state;
+    private $state = 'submitted';

     public function __toString(): string
     {

قم بتعديل اعدادات ال EasyAdmin لكي تتمكن من رؤية حالة التعليقات:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
--- a/config/packages/easy_admin.yaml
+++ b/config/packages/easy_admin.yaml
@@ -18,6 +18,7 @@ easy_admin:
                     - author
                     - { property: 'email', type: 'email' }
                     - { property: 'photoFilename', type: 'image', 'base_path': "/uploads/photos", label: 'Photo' }
+                    - state
                     - { property: 'createdAt', type: 'datetime' }
                 sort: ['createdAt', 'ASC']
                 filters: ['conference']
@@ -26,5 +27,6 @@ easy_admin:
                     - { property: 'conference' }
                     - { property: 'createdAt', type: datetime, type_options: { attr: { readonly: true } } }
                     - 'author'
+                    - { property: 'state' }
                     - { property: 'email', type: 'email' }
                     - text

لا تنسَ أيضاً تعديل الإختبارات (tests) عن طريق ضبط الحالة state في التركيبات (المثبتات fixtures):

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
--- a/src/DataFixtures/AppFixtures.php
+++ b/src/DataFixtures/AppFixtures.php
@@ -37,8 +37,16 @@ class AppFixtures extends Fixture
         $comment1->setAuthor('Fabien');
         $comment1->setEmail('[email protected]');
         $comment1->setText('This was a great conference.');
+        $comment1->setState('published');
         $manager->persist($comment1);

+        $comment2 = new Comment();
+        $comment2->setConference($amsterdam);
+        $comment2->setAuthor('Lucas');
+        $comment2->setEmail('[email protected]');
+        $comment2->setText('I think this one is going to be moderated.');
+        $manager->persist($comment2);
+
         $admin = new Admin();
         $admin->setRoles(['ROLE_ADMIN']);
         $admin->setUsername('admin');

بالنسبة لاختبارات الموجهات (controller)، قم بمحاكاة التحقق من صحة البيانات (simulate the validation):

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--- a/tests/Controller/ConferenceControllerTest.php
+++ b/tests/Controller/ConferenceControllerTest.php
@@ -2,6 +2,8 @@

 namespace App\Tests\Controller;

+use App\Repository\CommentRepository;
+use Doctrine\ORM\EntityManagerInterface;
 use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

 class ConferenceControllerTest extends WebTestCase
@@ -22,11 +24,17 @@ class ConferenceControllerTest extends WebTestCase
         $client->submitForm('Submit', [
             'comment_form[author]' => 'Fabien',
             'comment_form[text]' => 'Some feedback from an automated functional test',
-            'comment_form[email]' => '[email protected]',
+            'comment_form[email]' => $email = '[email protected]',
             'comment_form[photo]' => dirname(__DIR__, 2).'/public/images/under-construction.gif',
         ]);
         $this->assertResponseRedirects();
+
+        // simulate comment validation
+        $comment = self::$container->get(CommentRepository::class)->findOneByEmail($email);
+        $comment->setState('published');
+        self::$container->get(EntityManagerInterface::class)->flush();
+
         $client->followRedirect();
         $this->assertSelectorExists('div:contains("There are 2 comments")');
     }

من اختبارات ال PHPUnit، يمكنك الحصول علي اي خدمة (service) من ال container عن طريق self::$container->get(); كما أنه يمكنك الحصول علي الخدمات الغير عامة (non-public services).

فهم ال Messenger

إدارة التعليمات البرمجية (code) الغير متزامنة (asynchronous) في سيمفوني Symfony تعتبر وظيفة مكون ال Messenger:

1
$ symfony composer req messenger

عندما ينبغي تنفيذ بعض المنطق (logic) الغير متزامن (asynchronously)، أرسل رسالة إلي ناقل الرسول messenger bus. يقوم هذا الناقل بتخزين الرسالة في صف (قائمة انتظار queue) ويعود في الحال ليسمح باستئناف باقي العمليات بأسرع ما يمكن.

يعمل المستهلك (consumer) بشكل مستمر في الخلفية ليقوم بقرائة الرسائل من قائمة الانتظار (queue) ويقوم بتنفيذ المنطق المرتبط بهذه الرسائل (associated logic). ويمكن للمستهلك أن يعمل علي نفس الخادم (server) الذي يعمل عليه تطبيق الويب أو علي خادم منفصل.

ويشبه جداً الطريقة التي يتم بها معالجة طلبات ال HTTP، بإستثناء أنه ليس لدينا ردود فعل (responses).

برمجة معالج (Handler) الرسالة

لا يجب ان تحتوي الرسالة علي اي منطق (logic) فهي عبارة عن فئة كائن بيانات (data object class). سوف يتم تفكيكها (serialized) ليتم تخزينها في قائمة انتظار، لذلك قم بتخزين بيانات "بسيطة" قابلة للتفكيك (serializable).

قم بإنشاء فئة ال CommentMessage:

src/Message/CommentMessage.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace App\Message;

class CommentMessage
{
    private $id;
    private $context;

    public function __construct(int $id, array $context = [])
    {
        $this->id = $id;
        $this->context = $context;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function getContext(): array
    {
        return $this->context;
    }
}

في عالم الرسول (Messenger)، لا نملك موجهات (متحكمات controllers)، ولكن معالجات (handlers) رسائل.

قم بإنشاء ال CommentMessageHandler` تحت مساحة إسم (namespace) جديدة تسمي App\MessageHandler تعرف كيفية التعامل مع رسائل ال CommentMessage.

src/MessageHandler/CommentMessageHandler.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace App\MessageHandler;

use App\Message\CommentMessage;
use App\Repository\CommentRepository;
use App\SpamChecker;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;

class CommentMessageHandler implements MessageHandlerInterface
{
    private $spamChecker;
    private $entityManager;
    private $commentRepository;

    public function __construct(EntityManagerInterface $entityManager, SpamChecker $spamChecker, CommentRepository $commentRepository)
    {
        $this->entityManager = $entityManager;
        $this->spamChecker = $spamChecker;
        $this->commentRepository = $commentRepository;
    }

    public function __invoke(CommentMessage $message)
    {
        $comment = $this->commentRepository->find($message->getId());
        if (!$comment) {
            return;
        }

        if (2 === $this->spamChecker->getSpamScore($comment, $message->getContext())) {
            $comment->setState('spam');
        } else {
            $comment->setState('published');
        }

        $this->entityManager->flush();
    }
}

يعتبر ال MessageHandlerInterface علامة (marker) واجهة (interface). فإنه فقط يساعد سيمفوني Symfony علي عمل تسجيل تلقائي (auto-register) والتكوين (الاعداد) التلقائي (auto-configure) للفئة (class) كمعالج للمرسال (Messenger handler). حسب الإتفاق، فإن منطق (logic) المعالج (handle) يعيش في منهج (method) يسمي __invoke(). تلميح (type hint) ال CommentMessage الموجود علي واحد من ال argument لهذا المنهج (method) يخبر المرسال (Messenger) أي فئة (class) سيقوم بعالجتها.

قم بتعديل الموجهات (المتحكمات controller) لاستخدام النظام الجديد:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
--- a/src/Controller/ConferenceController.php
+++ b/src/Controller/ConferenceController.php
@@ -5,14 +5,15 @@ namespace App\Controller;
 use App\Entity\Comment;
 use App\Entity\Conference;
 use App\Form\CommentFormType;
+use App\Message\CommentMessage;
 use App\Repository\CommentRepository;
 use App\Repository\ConferenceRepository;
-use App\SpamChecker;
 use Doctrine\ORM\EntityManagerInterface;
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 use Symfony\Component\HttpFoundation\File\Exception\FileException;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Messenger\MessageBusInterface;
 use Symfony\Component\Routing\Annotation\Route;
 use Twig\Environment;

@@ -20,11 +21,13 @@ class ConferenceController extends AbstractController
 {
     private $twig;
     private $entityManager;
+    private $bus;

-    public function __construct(Environment $twig, EntityManagerInterface $entityManager)
+    public function __construct(Environment $twig, EntityManagerInterface $entityManager, MessageBusInterface $bus)
     {
         $this->twig = $twig;
         $this->entityManager = $entityManager;
+        $this->bus = $bus;
     }

     /**
@@ -40,7 +43,7 @@ class ConferenceController extends AbstractController
     /**
      * @Route("/conference/{slug}", name="conference")
      */
-    public function show(Request $request, Conference $conference, CommentRepository $commentRepository, SpamChecker $spamChecker, string $photoDir)
+    public function show(Request $request, Conference $conference, CommentRepository $commentRepository, string $photoDir)
     {
         $comment = new Comment();
         $form = $this->createForm(CommentFormType::class, $comment);
@@ -59,6 +62,7 @@ class ConferenceController extends AbstractController
             }

             $this->entityManager->persist($comment);
+            $this->entityManager->flush();

             $context = [
                 'user_ip' => $request->getClientIp(),
@@ -66,11 +70,8 @@ class ConferenceController extends AbstractController
                 'referrer' => $request->headers->get('referer'),
                 'permalink' => $request->getUri(),
             ];
-            if (2 === $spamChecker->getSpamScore($comment, $context)) {
-                throw new \RuntimeException('Blatant spam, go away!');
-            }

-            $this->entityManager->flush();
+            $this->bus->dispatch(new CommentMessage($comment->getId(), $context));

             return $this->redirectToRoute('conference', ['slug' => $conference->getSlug()]);
         }

بدلاً من الإعتماد علي Spam Checker، فإننا الأن نرسل رسالة إلي الناقل (bus). ومن ثم يقرر المعالج ماذا يفعل معها.

لقد حققنا شئ غير متوقع. لقد قمنا بنقل المنطق (logic) لفئة جديدة وفصلنا الموجه (المتحكم controller) عن ال Spam Checker، وهو المعالج (handler). إنها حالة استخدام مثالية للناقل. إختبر الكود، إنه يعمل. كل شئ مازال يتم بشكل متزامن، ولكن الكود ربما يكون أفضل بالفعل.

تقييد (التحكم) في التعليقات المعروضة

قم بتعديل الكود المستخدم في إظهار التعليقات لتجنب ظهور التعليقات الغير منشورة في الواجهة الامامية (frontend):

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
--- a/src/Repository/CommentRepository.php
+++ b/src/Repository/CommentRepository.php
@@ -25,7 +25,9 @@ class CommentRepository extends ServiceEntityRepository
     {
         return $this->createQueryBuilder('c')
             ->andWhere('c.conference = :conference')
+            ->andWhere('c.state = :state')
             ->setParameter('conference', $conference)
+            ->setParameter('state', 'published')
             ->orderBy('c.createdAt', 'DESC')
             ->setMaxResults($limit)
             ->setFirstResult($offset)

الذهاب المتزامن في الحقيقة

بشكل تلقائي، يتم النداء علي المعالجات بالتزامن. لاستخدامها بشكل غير متزامن، يجب عليك تهيئة (إعداد) أي صف (قائمة انتظار) لكل معالج في ملف الاعدادات الموجود في config/packages/messenger.yaml:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
--- a/config/packages/messenger.yaml
+++ b/config/packages/messenger.yaml
@@ -5,10 +5,10 @@ framework:

         transports:
             # https://symfony.com/doc/current/messenger.html#transport-configuration
-            # async: '%env(MESSENGER_TRANSPORT_DSN)%'
+            async: '%env(RABBITMQ_DSN)%'
             # failed: 'doctrine://default?queue_name=failed'
             # sync: 'sync://'

         routing:
             # Route your messages to the transports
-            # 'App\Message\YourMessage': async
+            App\Message\CommentMessage: async

ملف الاعدادت يخبر الناقل أن يرسل حالات (أمثلة instances) من App\Message\CommentMessage الي الصف الخاص بها الذي تم تعرفيه عن طريق DSN، المخزن في متغير بيئة العمل المسمي RABBITMQ_DSN.

إضافة RabbitMQ إلي Docker Stack

كما قد تكون خمنت، سوف نستخدم RabbitMQ:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -12,3 +12,7 @@ services:
     redis:
         image: redis:5-alpine
         ports: [6379]
+
+    rabbitmq:
+        image: rabbitmq:3.7-management
+        ports: [5672, 15672]

إعادة تشغل خدمات Docker

لإجبار Docker Compose لأخذ حاوية RabbitMQ container بعين الإعتبار، أوقف جميع الحاويات containers وقم بإعادة تشغيلهم:

1
2
$ docker-compose stop
$ docker-compose up -d
1
$ sleep 10

إستهلاك الرسائل

إذا حاولت إرسال تعليق جديد، لن يتم إستدعاء (إستخدام) محقق البيانات الفضولية (العشوائية spam checker) بعد الان. قم بإضافة error_log() في منهج (method) ال getSpamScore() للتأكيد. بدلاً من ذلك، هناك رسالة منتظرة في RabbitMQ، مستعدة ليتم استهلاكها (مناداتها) عن طريق بعض العمليات.

كما قد تخيلت، فإن سيمفوني يأتي معه أمر خاص بالمستهلك. قم بإستخدامه الأن:

1
$ symfony console messenger:consume async -vv

سيقوم علي الفور بإستهلاك الرسالة المرسلة الخاصة بالتعليق الذي تم إرساله:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 [OK] Consuming messages from transports "async".

 // The worker will automatically exit once it has received a stop signal via the messenger:stop-workers command.

 // Quit the worker with CONTROL-C.

11:30:20 INFO      [messenger] Received message App\Message\CommentMessage ["message" => App\Message\CommentMessage^ { …},"class" => "App\Message\CommentMessage"]
11:30:20 INFO      [http_client] Request: "POST https://80cea32be1f6.rest.akismet.com/1.1/comment-check"
11:30:20 INFO      [http_client] Response: "200 https://80cea32be1f6.rest.akismet.com/1.1/comment-check"
11:30:20 INFO      [messenger] Message App\Message\CommentMessage handled by App\MessageHandler\CommentMessageHandler::__invoke ["message" => App\Message\CommentMessage^ { …},"class" => "App\Message\CommentMessage","handler" => "App\MessageHandler\CommentMessageHandler::__invoke"]
11:30:20 INFO      [messenger] App\Message\CommentMessage was handled successfully (acknowledging to transport). ["message" => App\Message\CommentMessage^ { …},"class" => "App\Message\CommentMessage"]

عملية إستهلاك الرسالة تم تسجيلها، ولكن تحصل علي رد فوري علي وحدة التحكم (الكونسل) عن طريق تمرير أمر ال -vv. يمكنك حتي تحديد المناداة ل Akismet API.

لإقاف المستهلك، قم بضغط Ctrl+C.

إستكشاف واجهة الانترنت (الويب) الخاصة بادارة RabbitMQ

إذا اردت رؤية قوائم الانتظار والرسائل المتدفقة عبر RabbitMQ، قم بفتح واجهة ادارة الويب الخاصة به:

1
$ symfony open:local:rabbitmq

او من شريط تصحيح ادوات (debug toolbar) الويب:

إستخدم guest/guest للدخول إلي واجهة ادارة RabbitMQ:

تشغيل العمال (Workers) في الخلفية

بدلا من إطلاق المستهلك كل مرة نقوم بإضافة تعليق وإقافه فورا بعد ذلك، نريد ان نجعله يعمل بشكل مستمر بدون الحاجة الي العديد من شاشات التيرمنل او تبويبات (tabs) مفتوحة.

السيمفوني CLI يمكن ادارة مثل هذه الاوامر في الخلفية عن طريق اضافة علامة الشيطان (daemon flag) الي الامر عند التشغيل run.

قم بتشغيل مستهلك الرسالة مرة اخري، ولكن ارسله الي الخلفية:

1
$ symfony run -d --watch=config,src,templates,vendor symfony console messenger:consume async

خيار --watch يخبر سيمفوني بانه عليه ان يقوم بإعادة تشغيل الامر كلما كان هناك تغير في اي ملف يقع تحت ال config/، src/، templates/، او vendor/.

Note

لا تقم باستخدام -vv لانه سوف تجد رسائل متكررة في server:log (الرسائل المسجلة ورسائل وحدة التحكم (الكونسل)).

اذا توقف المستهلك عن العمل لاي سبب (حدود الذاكرة، مشكلة، ...)، سيتم إعادة تشغيله تلقائياً. ولو فشل المستهلك بسرعة أكبر مما يجب، سيمفوني CLI سوف يستسلم.

سوف تتدفق السجلات عن طريق symfony server:log مع جميع السجلات القادمة من PHP، خادم الويب، والتطبيق (الابلاكيشن):

1
$ symfony server:log

استخدم أمر ال server:status لسرد جميع العمال بالخلفية الذي يتم ادارتهم عن طريق المشروع الحالي:

1
2
3
4
$ symfony server:status

Web server listening on https://127.0.0.1:8000
  Command symfony console messenger:consume async running with PID 15774 (watching config/, src/, templates/)

لإقاف احد العمال، قم باقاف خادم الويب او اقتل ال PID المعطي عن طريق امر ال server:status:

1
$ kill 15774

إعادة محاولة الرسائل الفاشلة

ماذا لو ان ال Akismet سقط (وقع) أثناء استهلاك رسالة؟ لا يوجد اي تأثير علي الاشخاص الذين يقدمون (يرسلون) التعليقات، لكن الرسالة ضاعت ولم يتم التحقق من البيانات الفضولية.

الرسول (ماسنجر Messenger) يحتوي علي آلية إعادة المحاولة عندم تحدث مشكلة أثناء معالجة الرسالة. هيا نقوم بإعداده:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
--- a/config/packages/messenger.yaml
+++ b/config/packages/messenger.yaml
@@ -5,10 +5,17 @@ framework:

         transports:
             # https://symfony.com/doc/current/messenger.html#transport-configuration
-            async: '%env(RABBITMQ_DSN)%'
-            # failed: 'doctrine://default?queue_name=failed'
+            async:
+                dsn: '%env(RABBITMQ_DSN)%'
+                retry_strategy:
+                    max_retries: 3
+                    multiplier: 2
+
+            failed: 'doctrine://default?queue_name=failed'
             # sync: 'sync://'

+        failure_transport: failed
+
         routing:
             # Route your messages to the transports
             App\Message\CommentMessage: async

لو أن مشكلة حدثت أثناء معالجة الرسالة، المستهلك سيقوم بإعادة المحاولة ثلاث مرات قبل أن يستسلم. ولكن بدلاً من تجاهل الرسالة، سوف يقوم بتخزينها في مساحة تخرين دائمة، قائمة انتظار ال failed، التي تستخدم قاعدة بينات Doctrine.

إفحص الرسائل الفاشلة وقم باعادة محاولة إرسالهم عن طريق الاوامر التالية:

1
2
3
$ symfony console messenger:failed:show

$ symfony console messenger:failed:retry

نشر RabbitMQ

إضافة RabbitMQ إلي خادمات الانتاجية يمكن أن يتم عن طريق اضافة قائمة من الخدمات:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
--- a/.symfony/services.yaml
+++ b/.symfony/services.yaml
@@ -5,3 +5,8 @@ db:

 rediscache:
     type: redis:5.0
+
+queue:
+    type: rabbitmq:3.7
+    disk: 1024
+    size: S

أشر اليه في إعدادت حاوية الويب الرئيسية (web container) أيضاً وتمكين امتداد ال amqp الخاص بال PHP:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
--- a/.symfony.cloud.yaml
+++ b/.symfony.cloud.yaml
@@ -4,6 +4,7 @@ type: php:7.4

 runtime:
     extensions:
+        - amqp
         - pdo_pgsql
         - apcu
         - mbstring
@@ -22,6 +23,7 @@ variables:
 relationships:
     database: "db:postgresql"
     redis: "rediscache:redis"
+    rabbitmq: "queue:rabbitmq"

 web:
     locations:

عندما يتم تثبيت خدمة ال RabbitMQ في المشروع، يمكن الوصول الي واجهة إدارة الويب الخاصة به من خلال فتح النفق اولاً:

1
2
3
4
5
$ symfony tunnel:open
$ symfony open:remote:rabbitmq

# when done
$ symfony tunnel:close

تشغيل العمال علي SymfonyCloud

لإستهلاك الرسائل من RabbitMQ، نحتاج الي تشغيل امر ال messenger:consume بشكل مستمر. علي SymfonyCloud، هذا هو دور العامل:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
--- a/.symfony.cloud.yaml
+++ b/.symfony.cloud.yaml
@@ -46,3 +46,12 @@ hooks:
         set -x -e

         (>&2 symfony-deploy)
+
+workers:
+    messages:
+        commands:
+            start: |
+                set -x -e
+
+                (>&2 symfony-deploy)
+                php bin/console messenger:consume async -vv --time-limit=3600 --memory-limit=128M

مثل سيمفوني CLI، يقوم SymfonyCloud يقوم بادارة عمليات إعادة التشغيل والسجلات.

للحصول علي سجلات العامل، قم بأستخدام:

1
$ symfony logs --worker=messages all

  • « Previous خطوة 17: الإختبار
  • Next » خطوة 19: إتخاذ القرارات خلال سير العمل

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.