Messenger: Sync & Queued Message Handling

Messenger provides a message bus with the ability to send messages and then handle them immediately in your application or send them through transports (e.g. queues) to be handled later. To learn more deeply about it, read the Messenger component docs.

Messenger はメッセージ バスに、メッセージを送信してアプリケーションですぐに処理する機能、または後で処理するためにトランスポート (キューなど) を介して送信する機能を提供します。詳細については、Messenger コンポーネントのドキュメントを参照してください。

Installation

In applications using Symfony Flex, run this command to install messenger:

Symfony Flex を使用するアプリケーションでは、次のコマンドを実行してメッセンジャーをインストールします。
1
$ composer require symfony/messenger

Creating a Message & Handler

Messenger centers around two different classes that you'll create: (1) a message class that holds data and (2) a handler(s) class that will be called when that message is dispatched. The handler class will read the message class and perform one or more tasks.

Messenger は、作成する 2 つの異なるクラスを中心にしています。(1) データを保持するメッセージ クラスと、(2) メッセージがディスパッチされるときに呼び出されるハンドラ クラスです。ハンドラー クラスは、メッセージ クラスを読み取り、1 つ以上のタスクを実行します。

There are no specific requirements for a message class, except that it can be serialized:

シリアル化できることを除いて、メッセージ クラスに特定の要件はありません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Message/SmsNotification.php
namespace App\Message;

class SmsNotification
{
    private $content;

    public function __construct(string $content)
    {
        $this->content = $content;
    }

    public function getContent(): string
    {
        return $this->content;
    }
}

A message handler is a PHP callable, the recommended way to create it is to create a class that has the AsMessageHandler attribute and has an __invoke() method that's type-hinted with the message class (or a message interface):

メッセージ ハンドラーは PHP 呼び出し可能です。これを作成するための推奨される方法は、AsMessageHandler 属性を持ち、メッセージ クラス (またはメッセージ インターフェース) でタイプ ヒンティングされた __invoke() メソッドを持つクラスを作成することです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/MessageHandler/SmsNotificationHandler.php
namespace App\MessageHandler;

use App\Message\SmsNotification;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
class SmsNotificationHandler
{
    public function __invoke(SmsNotification $message)
    {
        // ... do some work - like sending an SMS message!
    }
}

Tip

ヒント

You can also use the #[AsMessageHandler] attribute on individual class methods. You may use the attribute on as many methods in a single class as you like, allowing you to group the handling of multiple related types of messages.

個々のクラスメソッドで #[AsMessageHandler] 属性を使用することもできます。この属性は、1 つのクラス内のメソッドで好きなだけ使用でき、関連する複数のタイプのメッセージの処理をグループ化できます。

6.1

6.1

Support for #[AsMessageHandler] on methods was introduced in Symfony 6.1.

メソッドでの #[AsMessageHandler] のサポートは、Symfony 6.1 で導入されました。

Thanks to autoconfiguration and the SmsNotification type-hint, Symfony knows that this handler should be called when an SmsNotification message is dispatched. Most of the time, this is all you need to do. But you can also manually configure message handlers. To see all the configured handlers, run:

自動構成と SmsNotificationtype-hint のおかげで、Symfony は、SmsNotification メッセージがディスパッチされたときにこのハンドラーを呼び出す必要があることを認識しています。ほとんどの場合、これだけで十分です。ただし、メッセージ ハンドラーを手動で構成することもできます。構成されたすべてのハンドラーを表示するには、次を実行します。
1
$ php bin/console debug:messenger

Dispatching the Message

You're ready! To dispatch the message (and call the handler), inject the messenger.default_bus service (via the MessageBusInterface), like in a controller:

準備ができた!メッセージをディスパッチする (そしてハンドラーを呼び出す) には、コントローラーのように (MessageBusInterface を介して) themessenger.default_bus サービスを注入します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Controller/DefaultController.php
namespace App\Controller;

use App\Message\SmsNotification;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Messenger\MessageBusInterface;

class DefaultController extends AbstractController
{
    public function index(MessageBusInterface $bus)
    {
        // will cause the SmsNotificationHandler to be called
        $bus->dispatch(new SmsNotification('Look! I created a message!'));

        // ...
    }
}

Transports: Async/Queued Messages

By default, messages are handled as soon as they are dispatched. If you want to handle a message asynchronously, you can configure a transport. A transport is capable of sending messages (e.g. to a queueing system) and then receiving them via a worker. Messenger supports multiple transports.

デフォルトでは、メッセージはディスパッチされるとすぐに処理されます。メッセージを非同期的に処理する場合は、トランスポートを構成できます。トランスポートは、メッセージを (キューイングシステムなどに) 送信し、ワーカーを介して受信することができます。 Messenger は複数のトランスポートをサポートしています。

Note

ノート

If you want to use a transport that's not supported, check out the Enqueue's transport, which supports things like Kafka and Google Pub/Sub.

サポートされていないトランスポートを使用する場合は、Kafka や Google Pub/Sub などをサポートするエンキューのトランスポートを確認してください。

A transport is registered using a "DSN". Thanks to Messenger's Flex recipe, your .env file already has a few examples.

トランスポートは「DSN」を使用して登録されます。 Messenger の Flex レシピのおかげで、.env ファイルにはすでにいくつかの例があります。
1
2
3
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
# MESSENGER_TRANSPORT_DSN=doctrine://default
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages

Uncomment whichever transport you want (or set it in .env.local). See Messenger: Sync & Queued Message Handling for more details.

必要なトランスポートのコメントを外します (または .env.local に設定します)。詳細については、Messenger: 同期およびキューに入れられたメッセージの処理を参照してください。

Next, in config/packages/messenger.yaml, let's define a transport called async that uses this configuration:

次に、config/packages/messenger.yaml で、この構成を使用する async というトランスポートを定義しましょう。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async: "%env(MESSENGER_TRANSPORT_DSN)%"

            # or expanded to configure more options
            #async:
            #    dsn: "%env(MESSENGER_TRANSPORT_DSN)%"
            #    options: []

Routing Messages to a Transport

Now that you have a transport configured, instead of handling a message immediately, you can configure them to be sent to a transport:

トランスポートを構成したので、メッセージをすぐに処理する代わりに、メッセージをトランスポートに送信するように構成できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async: "%env(MESSENGER_TRANSPORT_DSN)%"

        routing:
            # async is whatever name you gave your transport above
            'App\Message\SmsNotification': async

Thanks to this, the App\Message\SmsNotification will be sent to the async transport and its handler(s) will not be called immediately. Any messages not matched under routing will still be handled immediately, i.e. synchronously.

これにより、App\Message\SmsNotification が asynctransport に送信され、そのハンドラーがすぐに呼び出されることはありません。ルーティングで一致しないメッセージは、すぐに、つまり同期的に処理されます。

Note

ノート

You may use '*' as the message class. This will act as a default routing rule for any message not matched under routing. This is useful to ensure no message is handled synchronously by default.

メッセージクラスとして「*」を使用できます。これは、ルーティングで一致しないメッセージのデフォルトのルーティング ルールとして機能します。これは、デフォルトでメッセージが同期的に処理されないようにするのに役立ちます。

The only drawback is that '*' will also apply to the emails sent with the Symfony Mailer (which uses SendEmailMessage when Messenger is available). This could cause issues if your emails are not serializable (e.g. if they include file attachments as PHP resources/streams).

唯一の欠点は、'*' が Symfony Mailer (Messenger が利用可能な場合は SendEmailMessage を使用する) で送信された電子メールにも適用されることです。これは、電子メールがシリアル化できない場合 (たとえば、添付ファイルが PHP リソース/ストリームとして含まれている場合) に問題を引き起こす可能性があります。 .

You can also route classes by their parent class or interface. Or send messages to multiple transports:

親クラスまたはインターフェイスによってクラスをルーティングすることもできます。または、複数のトランスポートにメッセージを送信します:
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
# config/packages/messenger.yaml
framework:
    messenger:
        routing:
            # route all messages that extend this example base class or interface
            'App\Message\AbstractAsyncMessage': async
            'App\Message\AsyncMessageInterface': async

            'My\Message\ToBeSentToTwoSenders': [async, audit]

Note

ノート

If you configure routing for both a child and parent class, both rules are used. E.g. if you have an SmsNotification object that extends from Notification, both the routing for Notification and SmsNotification will be used.

子クラスと親クラスの両方にルーティングを設定すると、両方のルールが使用されます。例えば。 Notification から拡張する SmsNotification オブジェクトがある場合は、Notification と SmsNotification の両方のルーティングが使用されます。

Doctrine Entities in Messages

If you need to pass a Doctrine entity in a message, it's better to pass the entity's primary key (or whatever relevant information the handler actually needs, like email, etc.) instead of the object (otherwise you might see errors related to the Entity Manager):

メッセージで Doctrine エンティティを渡す必要がある場合は、オブジェクトの代わりにエンティティの主キー (またはハンドラーが実際に必要とする電子メールなどの関連情報) を渡すことをお勧めします (そうしないと、オブジェクトに関連するエラーが表示される可能性があります)。エンティティ マネージャー):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Message/NewUserWelcomeEmail.php
namespace App\Message;

class NewUserWelcomeEmail
{
    private $userId;

    public function __construct(int $userId)
    {
        $this->userId = $userId;
    }

    public function getUserId(): int
    {
        return $this->userId;
    }
}

Then, in your handler, you can query for a fresh object:

次に、ハンドラーで、新しいオブジェクトをクエリできます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/MessageHandler/NewUserWelcomeEmailHandler.php
namespace App\MessageHandler;

use App\Message\NewUserWelcomeEmail;
use App\Repository\UserRepository;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
class NewUserWelcomeEmailHandler
{
    private $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function __invoke(NewUserWelcomeEmail $welcomeEmail)
    {
        $user = $this->userRepository->find($welcomeEmail->getUserId());

        // ... send an email!
    }
}

This guarantees the entity contains fresh data.

これにより、エンティティに新しいデータが含まれていることが保証されます。

Handling Messages Synchronously

If a message doesn't match any routing rules, it won't be sent to any transport and will be handled immediately. In some cases (like when binding handlers to different transports), it's easier or more flexible to handle this explicitly: by creating a sync transport and "sending" messages there to be handled immediately:

メッセージがどのルーティング ルールにも一致しない場合、そのメッセージはどのトランスポートにも送信されず、すぐに処理されます。場合によっては (ハンドラーを異なるトランスポートにバインドする場合など)、これを明示的に処理する方が簡単または柔軟です: synctransport を作成し、そこにメッセージを「送信」して、すぐに処理します。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            # ... other transports

            sync: 'sync://'

        routing:
            App\Message\SmsNotification: sync

Creating your Own Transport

You can also create your own transport if you need to send or receive messages from something that is not supported. See How to Create Your own Messenger Transport.

サポートされていないものからメッセージを送受信する必要がある場合は、独自のトランスポートを作成することもできます。独自のメッセンジャー トランスポートを作成する方法を参照してください。

Consuming Messages (Running the Worker)

Once your messages have been routed, in most cases, you'll need to "consume" them. You can do this with the messenger:consume command:

メッセージがルーティングされたら、ほとんどの場合、メッセージを「消費」する必要があります。これは、messenger:consume コマンドで実行できます。
1
2
3
4
$ php bin/console messenger:consume async

# use -vv to see details about what's happening
$ php bin/console messenger:consume async -vv

The first argument is the receiver's name (or service id if you routed to a custom service). By default, the command will run forever: looking for new messages on your transport and handling them. This command is called your "worker".

最初の引数は受信者の名前 (カスタム サービスにルーティングした場合はサービス ID) です。デフォルトでは、コマンドは永久に実行されます: トランスポートで新しいメッセージを探して処理します。このコマンドは「ワーカー」と呼ばれます。

Tip

ヒント

To properly stop a worker, throw an instance of StopWorkerException.

ワーカーを適切に停止するには、StopWorkerException のインスタンスをスローします。

Deploying to Production

On production, there are a few important things to think about:

本番環境では、考慮すべき重要な点がいくつかあります。
Use a Process Manager like Supervisor or systemd to keep your worker(s) running
You'll want one or more "workers" running at all times. To do that, use a process control system like Supervisor or systemd.
常に 1 つ以上の「ワーカー」を実行する必要があります。これを行うには、Supervisor または systemd などのプロセス制御システムを使用します。
Don't Let Workers Run Forever
Some services (like Doctrine's EntityManager) will consume more memory over time. So, instead of allowing your worker to run forever, use a flag like messenger:consume --limit=10 to tell your worker to only handle 10 messages before exiting (then the process manager will create a new process). There are also other options like --memory-limit=128M and --time-limit=3600.
一部のサービス (Doctrine の EntityManager など) は、時間の経過とともにより多くのメモリを消費します。したがって、ワーカーを永久に実行できるようにする代わりに、フラグのようなメッセンジャー:consume --limit=10 を使用して、終了する前に 10 個のメッセージのみを処理するようにワーカーに指示します (その後、プロセス マネージャーが新しいプロセスを作成します)。 --memory-limit=128M や --time-limit=3600 などの他のオプションもあります。
Stopping Workers That Encounter Errors
If a worker dependency like your database server is down, or timeout is reached, you can try to add reconnect logic, or just quit the worker if it receives too many errors with the --failure-limit option of the messenger:consume command.
データベース サーバーなどのワーカーの依存関係がダウンしている場合、またはタイムアウトに達した場合は、再接続ロジックを追加するか、messenger:consume コマンドの --failure-limit オプションでエラーが多すぎる場合はワーカーを終了することができます。
Restart Workers on Deploy
Each time you deploy, you'll need to restart all your worker processes so that they see the newly deployed code. To do this, run messenger:stop-workers on deployment. This will signal to each worker that it should finish the message it's currently handling and should shut down gracefully. Then, the process manager will create new worker processes. The command uses the app cache internally - so make sure this is configured to use an adapter you like.
デプロイするたびに、新しくデプロイされたコードが表示されるように、すべてのワーカー プロセスを再起動する必要があります。これを行うには、messenger:stop-workerson デプロイメントを実行します。これは、現在処理しているメッセージを終了し、正常にシャットダウンする必要があることを各ワーカーに通知します。次に、プロセス マネージャが新しいワーカー プロセスを作成します。このコマンドは appcache を内部的に使用するため、好きなアダプターを使用するように構成されていることを確認してください。
Use the Same Cache Between Deploys
If your deploy strategy involves the creation of new target directories, you should set a value for the cache.prefix.seed configuration option in order to use the same cache namespace between deployments. Otherwise, the cache.app pool will use the value of the kernel.project_dir parameter as base for the namespace, which will lead to different namespaces each time a new deployment is made.
展開戦略に新しいターゲット ディレクトリの作成が含まれる場合は、展開間で同じキャッシュ名前空間を使用するために、cache.prefix.seed 構成オプションの値を設定する必要があります。それ以外の場合、cache.app プールはカーネルの値を使用します。 project_dir パラメーターを名前空間のベースとして使用します。これにより、新しい展開が行われるたびに異なる名前空間が作成されます。

Prioritized Transports

Sometimes certain types of messages should have a higher priority and be handled before others. To make this possible, you can create multiple transports and route different messages to them. For example:

場合によっては、特定のタイプのメッセージの優先度を高くして、他のメッセージよりも先に処理する必要があります。これを可能にするために、複数のトランスポートを作成し、それらに異なるメッセージをルーティングできます。例えば:
  • YAML
    YAML
  • XML
    XML
  • PHP
    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
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async_priority_high:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                options:
                    # queue_name is specific to the doctrine transport
                    queue_name: high

                    # for AMQP send to a separate exchange then queue
                    #exchange:
                    #    name: high
                    #queues:
                    #    messages_high: ~
                    # or redis try "group"
            async_priority_low:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                options:
                    queue_name: low

        routing:
            'App\Message\SmsNotification': async_priority_low
            'App\Message\NewUserWelcomeEmail': async_priority_high

You can then run individual workers for each transport or instruct one worker to handle messages in a priority order:

次に、トランスポートごとに個々のワーカーを実行するか、1 つのワーカーに優先順位に従ってメッセージを処理するように指示できます。
1
$ php bin/console messenger:consume async_priority_high async_priority_low

The worker will always first look for messages waiting on async_priority_high. If there are none, then it will consume messages from async_priority_low.

ワーカーは常に、async_priority_high で待機しているメッセージを最初に探します。何もない場合は、async_priority_low からのメッセージを消費します。

Limit Consuming to Specific Queues

Some transports (notably AMQP) have the concept of exchanges and queues. A Symfony transport is always bound to an exchange. By default, the worker consumes from all queues attached to the exchange of the specified transport. However, there are use cases to want a worker to only consume from specific queues.

一部のトランスポート (特に AMQP) には、交換とキューの概念があります。 Symfonytransport は常に交換にバインドされます。デフォルトでは、ワーカーは指定されたトランスポートの交換に接続されたすべてのキューから消費します。ただし、ワーカーが特定のキューからのみ消費するようにするユースケースがあります。

You can limit the worker to only process messages from specific queue(s):

特定のキューからのメッセージのみを処理するようにワーカーを制限できます。
1
2
3
4
$ php bin/console messenger:consume my_transport --queues=fasttrack

# you can pass the --queues option more than once to process multiple queues
$ php bin/console messenger:consume my_transport --queues=fasttrack1 --queues=fasttrack2

Note

ノート

To allow using the queues option, the receiver must implement the QueueReceiverInterface.

queues オプションを使用できるようにするには、受信側で QueueReceiverInterface を実装する必要があります。

Checking the Number of Queued Messages Per Transport

Run the messenger:stats command to know how many messages are in the "queues" of some or all transports:

messenger:stats コマンドを実行して、一部またはすべてのトランスポートの「キュー」にあるメッセージの数を確認します。
1
2
3
4
5
# displays the number of queued messages in all transports
$ php bin/console messenger:stats

# shows stats only for some transports
$ php bin/console messenger:stats my_transport_name other_transport_name

Note

ノート

In order for this command to work, the configured transport's receiver must implement MessageCountAwareInterface.

このコマンドが機能するためには、構成されたトランスポートのレシーバーが MessageCountAwareInterface を実装する必要があります。

6.2

6.2

The messenger:stats command was introduced in Symfony 6.2.

Messenger:stats コマンドは Symfony 6.2 で導入されました。

Supervisor Configuration

Supervisor is a great tool to guarantee that your worker process(es) is always running (even if it closes due to failure, hitting a message limit or thanks to messenger:stop-workers). You can install it on Ubuntu, for example, via:

スーパーバイザーは、ワーカー プロセスが常に実行されていることを保証するための優れたツールです (失敗によりプロセスが終了した場合でも、messenger:stop-workers のおかげでメッセージ リミッターにヒットします)。たとえば、次の方法でUbuntuにインストールできます。
1
$ sudo apt-get install supervisor

Supervisor configuration files typically live in a /etc/supervisor/conf.d directory. For example, you can create a new messenger-worker.conf file there to make sure that 2 instances of messenger:consume are running at all times:

スーパーバイザー構成ファイルは通常、/etc/supervisor/conf.d ディレクトリにあります。たとえば、messenger:consume の 2 つのインスタンスが常に実行されていることを確認するために、そこに新しい messenger-worker.conf ファイルを作成できます。
1
2
3
4
5
6
7
8
9
10
;/etc/supervisor/conf.d/messenger-worker.conf
[program:messenger-consume]
command=php /path/to/your/app/bin/console messenger:consume async --time-limit=3600
user=ubuntu
numprocs=2
startsecs=0
autostart=true
autorestart=true
startretries=10
process_name=%(program_name)s_%(process_num)02d

Change the async argument to use the name of your transport (or transports) and user to the Unix user on your server.

async 引数を変更して、トランスポート (またはトランスポート) の名前を使用し、ユーザーをサーバー上の Unix ユーザーに変更します。

Caution

注意

During a deployment, something might be unavailable (e.g. the database) causing the consumer to fail to start. In this situation, Supervisor will try startretries number of times to restart the command. Make sure to change this setting to avoid getting the command in a FATAL state, which will never restart again.

展開中に、何か (データベースなど) が利用できなくなり、コンシューマーの起動に失敗することがあります。この場合、Supervisor は startretries 回数だけコマンドを再起動しようとします。この設定を変更して、コマンドが FATAL 状態になり、二度と再起動しないようにしてください。

Each restart, Supervisor increases the delay by 1 second. For instance, if the value is 10, it will wait 1 sec, 2 sec, 3 sec, etc. This gives the service a total of 55 seconds to become available again. Increase the startretries setting to cover the maximum expected downtime.

再起動するたびに、Supervisor は遅延を 1 秒ずつ増やします。たとえば、値が 10 の場合、1 秒、2 秒、3 秒などと待機します。これにより、サービスが再び利用可能になるまで合計 55 秒かかります。予想される最大ダウンタイムに対応できるように、startretries 設定を増やします。

If you use the Redis Transport, note that each worker needs a unique consumer name to avoid the same message being handled by multiple workers. One way to achieve this is to set an environment variable in the Supervisor configuration file, which you can then refer to in messenger.yaml (see Redis section above):

Redis Transport を使用する場合、同じメッセージが複数のワーカーによって処理されるのを避けるために、各ワーカーには一意のコンシューマー名が必要であることに注意してください。これを実現する 1 つの方法は、スーパーバイザー構成ファイルに環境変数を設定することです。これは、messenger.yaml で参照できます (上記の Redis セクションを参照)。
1
environment=MESSENGER_CONSUMER_NAME=%(program_name)s_%(process_num)02d

Next, tell Supervisor to read your config and start your workers:

次に、Supervisor に設定を読み込んでワーカーを起動するように指示します。
1
2
3
4
5
$ sudo supervisorctl reread

$ sudo supervisorctl update

$ sudo supervisorctl start messenger-consume:*

See the Supervisor docs for more details.

詳細については、スーパーバイザーのドキュメントを参照してください。

Graceful Shutdown

If you install the PCNTL PHP extension in your project, workers will handle the SIGTERM POSIX signal to finish processing their current message before terminating.

プロジェクトに PCNTL PHP 拡張機能をインストールすると、ワーカーは SIGTERM POSIX シグナルを処理して、終了する前に現在のメッセージの処理を終了します。

In some cases the SIGTERM signal is sent by Supervisor itself (e.g. stopping a Docker container having Supervisor as its entrypoint). In these cases you need to add a stopwaitsecs key to the program configuration (with a value of the desired grace period in seconds) in order to perform a graceful shutdown:

場合によっては、SIGTERM シグナルが Supervisor 自体によって送信されます (たとえば、Supervisor をエントリポイントとして持つ Docker コンテナーを停止する場合)。これらの場合、正常なシャットダウンを実行するために、プログラム構成に stopwaitsecs キーを追加する必要があります (希望する猶予期間の値を秒単位で指定します)。
1
2
[program:x]
stopwaitsecs=20

Systemd Configuration

While Supervisor is a great tool, it has the disadvantage that you need system access to run it. Systemd has become the standard on most Linux distributions, and has a good alternative called user services.

Supervisor は優れたツールですが、実行するには systemaccess が必要であるという欠点があります。 Systemd はほとんどの Linux ディストリビューションで標準になっており、ユーザー サービスと呼ばれる優れた代替手段があります。

Systemd user service configuration files typically live in a ~/.config/systemd/user directory. For example, you can create a new messenger-worker.service file. Or a messenger-worker@.service file if you want more instances running at the same time:

通常、Systemd ユーザー サービス構成ファイルは ~/.config/systemd/userdirectory にあります。たとえば、新しい Messenger-worker.service ファイルを作成できます。または、より多くのインスタンスを同時に実行する場合は、amessenger-worker@.service ファイル:
1
2
3
4
5
6
7
8
9
10
[Unit]
Description=Symfony messenger-consume %i

[Service]
ExecStart=php /path/to/your/app/bin/console messenger:consume async --time-limit=3600
Restart=always
RestartSec=30

[Install]
WantedBy=default.target

Now, tell systemd to enable and start one worker:

ここで、1 つのワーカーを有効にして開始するように systemd に指示します。
1
2
3
4
5
6
$ systemctl --user enable messenger-worker@1.service
$ systemctl --user start messenger-worker@1.service

# to enable and start 20 workers
$ systemctl --user enable messenger-worker@{1..20}.service
$ systemctl --user start messenger-worker@{1..20}.service

If you change your service config file, you need to reload the daemon:

サービス構成ファイルを変更した場合は、デーモンをリロードする必要があります。
1
$ systemctl --user daemon-reload

To restart all your consumers:

すべてのコンシューマを再起動するには:
1
$ systemctl --user restart messenger-consume@*.service

The systemd user instance is only started after the first login of the particular user. Consumer often need to start on system boot instead. Enable lingering on the user to activate that behavior:

systemd ユーザー インスタンスは、特定のユーザーが最初にログインした後にのみ開始されます。多くの場合、消費者はシステムの起動時に起動する必要があります。ユーザーの残留を有効にして、その動作を有効にします。
1
$ loginctl enable-linger <your-username>

Logs are managed by journald and can be worked with using the journalctl command:

ログは、journald によって管理され、journalctl コマンドを使用して操作できます。
1
2
3
4
5
6
7
8
# follow logs of consumer nr 11
$ journalctl -f --user-unit messenger-consume@11.service

# follow logs of all consumers
$ journalctl -f --user-unit messenger-consume@*

# follow all logs from your user services
$ journalctl -f _UID=$UID

See the systemd docs for more details.

詳細については、systemd のドキュメントを参照してください。

Note

ノート

You either need elevated privileges for the journalctl command, or add your user to the systemd-journal group:

journalctl コマンドには昇格した権限が必要か、ユーザーを systemd-journal グループに追加します。
1
$ sudo usermod -a -G systemd-journal <your-username>

Stateless Worker

PHP is designed to be stateless, there are no shared resources across different requests. In HTTP context PHP cleans everything after sending the response, so you can decide to not take care of services that may leak memory.

PHP はステートレスになるように設計されており、異なるリクエスト間でリソースを共有することはありません。 HTTP コンテキストでは、PHP は応答を送信した後にすべてをクリーンアップするため、メモリ リークの可能性があるサービスを処理しないことを決定できます。

On the other hand, it's common for workers to process messages sequentially in long-running CLI processes which don't finish after processing a single message. Beware about service states to prevent information and/or memory leakage as Symfony will inject the same instance of a service in all messages, preserving the internal state of the services.

一方で、ワーカーが単一のメッセージを処理した後に終了しない長時間実行 CLI プロセスでメッセージを順次処理することは一般的です。Symfony はサービスの同じインスタンスを注入するため、情報やメモリのリークを防ぐためにサービスの状態に注意してください。すべてのメッセージで、サービスの内部状態を維持します。

However, certain Symfony services, such as the Monolog fingers crossed handler, leak by design. Symfony provides a service reset feature to solve this problem. When resetting the container automatically between two messages, Symfony looks for any services implementing ResetInterface (including your own services) and calls their reset() method so they can clean their internal state.

ただし、Monologfinger クロス ハンドラーなどの特定の Symfony サービスは、設計によりリークします。Symfony は、この問題を解決するサービス リセット機能を提供します。 2 つのメッセージ間でコンテナを自動的にリセットする場合、Symfony は ResetInterface を実装するサービス (独自のサービスを含む) を探し、それらの reset() メソッドを呼び出して内部状態を消去できるようにします。

If a service is not stateless and you want to reset its properties after each message, then the service must implement ResetInterface where you can reset the properties in the reset() method.

サービスがステートレスではなく、各メッセージの後にそのプロパティをリセットする場合、サービスは、reset() メソッドでプロパティをリセットできる ResetInterface を実装する必要があります。

If you don't want to reset the container, add the --no-reset option when running the messenger:consume command.

コンテナーをリセットしたくない場合は、messenger:consume コマンドの実行時に --no-reset オプションを追加します。

6.1

6.1

In Symfony versions previous to 6.1, the service container didn't reset automatically between messages and you had to set the framework.messenger.reset_on_message option to true.

6.1 より前のバージョンの Symfony では、サービス コンテナーはメッセージ間で自動的にリセットされず、framework.messenger.reset_on_message オプションを true に設定する必要がありました。

Retries & Failures

If an exception is thrown while consuming a message from a transport it will automatically be re-sent to the transport to be tried again. By default, a message will be retried 3 times before being discarded or sent to the failure transport. Each retry will also be delayed, in case the failure was due to a temporary issue. All of this is configurable for each transport:

トランスポートからのメッセージの消費中に例外がスローされた場合、メッセージは自動的にトランスポートに再送信され、再試行されます。デフォルトでは、メッセージは破棄されるか失敗トランスポートに送信される前に 3 回再試行されます。失敗が一時的な問題によるものであった場合、各再試行も遅れます。これはすべて、トランスポートごとに構成可能です。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async_priority_high:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'

                # default configuration
                retry_strategy:
                    max_retries: 3
                    # milliseconds delay
                    delay: 1000
                    # causes the delay to be higher before each retry
                    # e.g. 1 second delay, 2 seconds, 4 seconds
                    multiplier: 2
                    max_delay: 0
                    # override all of this with a service that
                    # implements Symfony\Component\Messenger\Retry\RetryStrategyInterface
                    # service: null

Tip

ヒント

Symfony triggers a WorkerMessageRetriedEvent when a message is retried so you can run your own logic.

Symfony は、メッセージが再試行されると WorkerMessageRetriedEvent をトリガーするため、独自のロジックを実行できます。

Note

ノート

Thanks to SerializedMessageStamp, the serialized form of the message is saved, which prevents to serialize it again if the message is later retried.

SerializedMessageStamp のおかげで、メッセージのシリアル化された形式が保存されます。これにより、メッセージが後で再試行された場合に再度シリアル化することができなくなります。

6.1

6.1

The SerializedMessageStamp class was introduced in Symfony 6.1.

SerializedMessageStamp クラスは Symfony 6.1 で導入されました。

Avoiding Retrying

Sometimes handling a message might fail in a way that you know is permanent and should not be retried. If you throw UnrecoverableMessageHandlingException, the message will not be retried.

場合によっては、メッセージの処理が永続的で再試行すべきではないことがわかっている方法で失敗することがあります。 UnrecoverableMessageHandlingException をスローすると、メッセージは再試行されません。

Forcing Retrying

Sometimes handling a message must fail in a way that you know is temporary and must be retried. If you throw RecoverableMessageHandlingException, the message will always be retried infinitely and max_retries setting will be ignored.

場合によっては、メッセージの処理が一時的なものであり、再試行する必要があることがわかっている方法で失敗する必要があります。 RecoverableMessageHandlingException をスローすると、メッセージは常に無限に再試行され、max_retries 設定は無視されます。

Saving & Retrying Failed Messages

If a message fails it is retried multiple times (max_retries) and then will be discarded. To avoid this happening, you can instead configure a failure_transport:

メッセージが失敗すると、複数回 (max_retries) 再試行された後、破棄されます。これを回避するために、代わりに failure_transport を構成できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
# config/packages/messenger.yaml
framework:
    messenger:
        # after retrying, messages will be sent to the "failed" transport
        failure_transport: failed

        transports:
            # ... other transports

            failed: 'doctrine://default?queue_name=failed'

In this example, if handling a message fails 3 times (default max_retries), it will then be sent to the failed transport. While you can use messenger:consume failed to consume this like a normal transport, you'll usually want to manually view the messages in the failure transport and choose to retry them:

この例では、メッセージの処理が 3 回失敗した場合 (デフォルトの max_retries)、メッセージは失敗したトランスポートに送信されます。通常のトランスポートのようにmessenger:consume を使用してこれを消費することはできますが、通常は、失敗したトランスポートでメッセージを手動で表示し、再試行することを選択します。
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
# see all messages in the failure transport with a default limit of 50
$ php bin/console messenger:failed:show

# see the 10 first messages
$ php bin/console messenger:failed:show --max=10

# see only MyClass messages
$ php bin/console messenger:failed:show --class-filter='MyClass'

# see the number of messages by message class
$ php bin/console messenger:failed:show --stats

# see details about a specific failure
$ php bin/console messenger:failed:show 20 -vv

# view and retry messages one-by-one
$ php bin/console messenger:failed:retry -vv

# retry specific messages
$ php bin/console messenger:failed:retry 20 30 --force

# remove a message without retrying it
$ php bin/console messenger:failed:remove 20

# remove messages without retrying them and show each message before removing it
$ php bin/console messenger:failed:remove 20 30 --show-messages

6.2

6.2

The --class-filter and --stats options were introduced in Symfony 6.2.

--class-filter および --stats オプションは Symfony 6.2 で導入されました。

If the message fails again, it will be re-sent back to the failure transport due to the normal retry rules. Once the max retry has been hit, the message will be discarded permanently.

メッセージが再び失敗した場合、通常の再試行ルールにより、メッセージは失敗したトランスポートに再送信されます。 maxretry に達すると、メッセージは完全に破棄されます。

Multiple Failed Transports

Sometimes it is not enough to have a single, global failed transport configured because some messages are more important than others. In those cases, you can override the failure transport for only specific transports:

一部のメッセージが他のメッセージよりも重要であるため、1 つのグローバルな失敗したトランスポートを構成するだけでは不十分な場合があります。そのような場合、特定のトランスポートのみの障害トランスポートをオーバーライドできます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# config/packages/messenger.yaml
framework:
    messenger:
        # after retrying, messages will be sent to the "failed" transport
        # by default if no "failed_transport" is configured inside a transport
        failure_transport: failed_default

        transports:
            async_priority_high:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                failure_transport: failed_high_priority

            # since no failed transport is configured, the one used will be
            # the global "failure_transport" set
            async_priority_low:
                dsn: 'doctrine://default?queue_name=async_priority_low'

            failed_default: 'doctrine://default?queue_name=failed_default'
            failed_high_priority: 'doctrine://default?queue_name=failed_high_priority'

If there is no failure_transport defined globally or on the transport level, the messages will be discarded after the number of retries.

グローバルまたはトランスポート レベルで定義された failure_transport がない場合、メッセージは再試行回数後に破棄されます。

The failed commands have an optional option --transport to specify the failure_transport configured at the transport level.

失敗したコマンドには、オプションのオプション --transport があり、トランスポート レベルで構成された failure_transport を指定します。
1
2
3
4
5
6
7
8
# see all messages in "failure_transport" transport
$ php bin/console messenger:failed:show --transport=failure_transport

# retry specific messages from "failure_transport"
$ php bin/console messenger:failed:retry 20 30 --transport=failure_transport --force

# remove a message without retrying it from "failure_transport"
$ php bin/console messenger:failed:remove 20 --transport=failure_transport

Transport Configuration

Messenger supports a number of different transport types, each with their own options. Options can be passed to the transport via a DSN string or configuration.

Messenger はさまざまなトランスポート タイプをサポートしており、それぞれに独自のオプションがあります。オプションは、DSN 文字列または構成を介してトランスポートに渡すことができます。
1
2
# .env
MESSENGER_TRANSPORT_DSN=amqp://localhost/%2f/messages?auto_setup=false
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            my_transport:
                dsn: "%env(MESSENGER_TRANSPORT_DSN)%"
                options:
                    auto_setup: false

Options defined under options take precedence over ones defined in the DSN.

options で定義されたオプションは、DSN で定義されたオプションよりも優先されます。

AMQP Transport

The AMQP transport uses the AMQP PHP extension to send messages to queues like RabbitMQ. Install it by running:

AMQP トランスポートは、AMQP PHP 拡張機能を使用して、RabbitMQ などのキューにメッセージを送信します。次を実行してインストールします。
1
$ composer require symfony/amqp-messenger

The AMQP transport DSN may looks like this:

AMQP トランスポート DSN は次のようになります。
1
2
3
4
5
# .env
MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages

# or use the AMQPS protocol
MESSENGER_TRANSPORT_DSN=amqps://guest:guest@localhost/%2f/messages

If you want to use TLS/SSL encrypted AMQP, you must also provide a CA certificate. Define the certificate path in the amqp.cacert PHP.ini setting (e.g. amqp.cacert = /etc/ssl/certs) or in the cacert parameter of the DSN (e.g amqps://localhost?cacert=/etc/ssl/certs/).

TLS/SSL 暗号化 AMQP を使用する場合は、CA 証明書も提供する必要があります。amqp.cacert PHP.ini 設定 (例: amqp.cacert = /etc/ssl/certs) または cacert パラメータで証明書パスを定義します。 DSN (例: amqps://localhost?cacert=/etc/ssl/certs/)。

The default port used by TLS/SSL encrypted AMQP is 5671, but you can overwrite it in the port parameter of the DSN (e.g. amqps://localhost?cacert=/etc/ssl/certs/&port=12345).

Note

ノート

By default, the transport will automatically create any exchanges, queues and binding keys that are needed. That can be disabled, but some functionality may not work correctly (like delayed queues). To not autocreate any queues, you can configure a transport with queues: [].

デフォルトでは、トランスポートは必要な交換、キュー、およびバインディング キーを自動的に作成します。これは無効にできますが、一部の機能が正しく動作しない場合があります (遅延キューなど)。キューを自動作成しないようにするには、キュー [] を使用してトランスポートを構成できます。

Note

ノート

You can limit the consumer of an AMQP transport to only process messages from some queues of an exchange. See Messenger: Sync & Queued Message Handling.

AMQP トランスポートのコンシューマを、交換の一部のキューからのメッセージのみを処理するように制限できます。 Messenger: 同期およびキューに入れられたメッセージの処理を参照してください。

The transport has a number of other options, including ways to configure the exchange, queues binding keys and more. See the documentation on Connection.

トランスポートには、交換を構成する方法、キーをバインドするキューなど、他にも多くのオプションがあります。ドキュメント onConnection を参照してください。

The transport has a number of options:

トランスポートにはいくつかのオプションがあります。
Option Description Default
auto_setup Whether the exchanges and queues should be created automatically during send / get. true
cacert Path to the CA cert file in PEM format.  
cert Path to the client certificate in PEM format.  
channel_max Specifies highest channel number that the server permits. 0 means standard extension limit  
confirm_timeout Timeout in seconds for confirmation; if none specified, transport will not wait for message confirmation. Note: 0 or greater seconds. May be fractional.  
connect_timeout Connection timeout. Note: 0 or greater seconds. May be fractional.  
frame_max The largest frame size that the server proposes for the connection, including frame header and end-byte. 0 means standard extension limit (depends on librabbimq default frame size limit)  
heartbeat The delay, in seconds, of the connection heartbeat that the server wants. 0 means the server does not want a heartbeat. Note, librabbitmq has limited heartbeat support, which means heartbeats checked only during blocking calls.  
host Hostname of the AMQP service  
key Path to the client key in PEM format.  
login Username to use to connect the AMQP service  
password Password to use to connect to the AMQP service  
persistent   'false'
port Port of the AMQP service  
read_timeout Timeout in for income activity. Note: 0 or greater seconds. May be fractional.  
retry    
sasl_method    
connection_name For custom connection names (requires at least version 1.10 of the PHP AMQP extension)  
verify Enable or disable peer verification. If peer verification is enabled then the common name in the server certificate must match the server name. Peer verification is enabled by default.  
vhost Virtual Host to use with the AMQP service  
write_timeout Timeout in for outcome activity. Note: 0 or greater seconds. May be fractional.  
delay[queue_name_pattern] Pattern to use to create the queues delay_%exchange_name%_%routing_key%_%delay%
delay[exchange_name] Name of the exchange to be used for the delayed/retried messages delays
queues[name][arguments] Extra arguments  
queues[name][binding_arguments] Arguments to be used while binding the queue.  
queues[name][binding_keys] The binding keys (if any) to bind to this queue  
queues[name][flags] Queue flags AMQP_DURABLE
exchange[arguments] Extra arguments for the exchange (e.g. alternate-exchange)  
exchange[default_publish_routing_key] Routing key to use when publishing, if none is specified on the message  
exchange[flags] Exchange flags AMQP_DURABLE
exchange[name] Name of the exchange  
exchange[type] Type of exchange fanout

6.1

6.1

The connection_name option was introduced in Symfony 6.1.

connection_name オプションは Symfony 6.1 で導入されました。

You can also configure AMQP-specific settings on your message by adding AmqpStamp to your Envelope:

エンベロープに AmqpStamp を追加して、メッセージに AMQP 固有の設定を構成することもできます。
1
2
3
4
5
6
7
use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpStamp;
// ...

$attributes = [];
$bus->dispatch(new SmsNotification(), [
    new AmqpStamp('custom-routing-key', AMQP_NOPARAM, $attributes),
]);

Caution

注意

The consumers do not show up in an admin panel as this transport does not rely on \AmqpQueue::consume() which is blocking. Having a blocking receiver makes the --time-limit/--memory-limit options of the messenger:consume command as well as the messenger:stop-workers command inefficient, as they all rely on the fact that the receiver returns immediately no matter if it finds a message or not. The consume worker is responsible for iterating until it receives a message to handle and/or until one of the stop conditions is reached. Thus, the worker's stop logic cannot be reached if it is stuck in a blocking call.

このトランスポートはブロックしている on\AmqpQueue::consume() に依存しないため、コンシューマーは管理パネルに表示されません。受信側をブロックすると、messenger:consume コマンドと messenger:stop-workers コマンドの --time-limit/--memory-limit オプションが非効率になります。メッセージかどうか。 consumerworker は、処理するメッセージを受信するまで、および/または停止条件の 1 つに到達するまで反復する責任があります。したがって、ワーカーがブロッキング コールでスタックしている場合、ワーカーの停止ロジックに到達できません。

Doctrine Transport

The Doctrine transport can be used to store messages in a database table. Install it by running:

Doctrine トランスポートを使用して、データベース テーブルにメッセージを保存できます。次のコマンドを実行してインストールします。
1
$ composer require symfony/doctrine-messenger

The Doctrine transport DSN may looks like this:

Doctrine トランスポート DSN は次のようになります。
1
2
# .env
MESSENGER_TRANSPORT_DSN=doctrine://default

The format is doctrine://<connection_name>, in case you have multiple connections and want to use one other than the "default". The transport will automatically create a table named messenger_messages.

複数の接続があり、「デフォルト」以外の接続を使用したい場合に備えて、形式は doctrine:// です。トランスポートは、messenger_messages という名前のテーブルを自動的に作成します。

Or, to create the table yourself, set the auto_setup option to false and generate a migration.

または、自分でテーブルを作成するには、auto_setup オプションを false に設定し、移行を生成します。

Tip

ヒント

To avoid tools like Doctrine Migrations from trying to remove this table because it's not part of your normal schema, you can set the schema_filter option:

通常のスキーマの一部ではないため、Doctrine Migrations のようなツールがこのテーブルを削除しようとするのを避けるために、 schema_filter オプションを設定できます:
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
# config/packages/doctrine.yaml
doctrine:
    dbal:
        schema_filter: '~^(?!messenger_messages)~'

Caution

注意

The datetime property of the messages stored in the database uses the timezone of the current system. This may cause issues if multiple machines with different timezone configuration use the same storage.

データベースに格納されたメッセージの datetime プロパティは、現在のシステムのタイムゾーンを使用します。これにより、タイムゾーン構成が異なる複数のマシンが同じストレージを使用する場合に問題が発生する可能性があります。

The transport has a number of options:

トランスポートにはいくつかのオプションがあります。
Option Description Default
table_name Name of the table messenger_messages
queue_name Name of the queue (a column in the table, to use one table for multiple transports) default
redeliver_timeout Timeout before retrying a message that's in the queue but in the "handling" state (if a worker stopped for some reason, this will occur, eventually you should retry the message) - in seconds. 3600
auto_setup Whether the table should be created automatically during send / get. true

Note

ノート

Set redeliver_timeout to a greater value than your slowest message duration. Otherwise, some messages will start a second time while the first one is still being handled.

redeliver_timeout を、最も遅い messageduration よりも大きな値に設定します。そうしないと、最初のメッセージがまだ処理されている間に、一部のメッセージが 2 度目に開始されます。

When using PostgreSQL, you have access to the following options to leverage the LISTEN/NOTIFY feature. This allow for a more performant approach than the default polling behavior of the Doctrine transport because PostgreSQL will directly notify the workers when a new message is inserted in the table.

PostgreSQL を使用する場合、次のオプションにアクセスして LISTEN/NOTIFY 機能を利用できます。これにより、新しいメッセージがテーブルに挿入されると PostgreSQL がワーカーに直接通知するため、Doctrine トランスポートのデフォルトのポーリング動作よりもパフォーマンスの高いアプローチが可能になります。
Option Description Default
use_notify Whether to use LISTEN/NOTIFY. true
check_delayed_interval The interval to check for delayed messages, in milliseconds. Set to 0 to disable checks. 60000
get_notify_timeout The length of time to wait for a response when calling PDO::pgsqlGetNotify, in milliseconds. 0

Beanstalkd Transport

The Beanstalkd transport sends messages directly to a Beanstalkd work queue. Install it by running:

Beanstalkd トランスポートは、メッセージを Beanstalkd 作業キューに直接送信します。次を実行してインストールします。
1
$ composer require symfony/beanstalkd-messenger

The Beanstalkd transport DSN may looks like this:

Beanstalkd トランスポート DSN は次のようになります。
1
2
3
4
5
# .env
MESSENGER_TRANSPORT_DSN=beanstalkd://localhost:11300?tube_name=foo&timeout=4&ttr=120

# If no port, it will default to 11300
MESSENGER_TRANSPORT_DSN=beanstalkd://localhost

The transport has a number of options:

トランスポートにはいくつかのオプションがあります。
Option Description Default
tube_name Name of the queue default
timeout Message reservation timeout - in seconds. 0 (will cause the server to immediately return either a response or a TransportException will be thrown)
ttr The message time to run before it is put back in the ready queue - in seconds. 90

Redis Transport

The Redis transport uses streams to queue messages. This transport requires the Redis PHP extension (>=4.3) and a running Redis server (^5.0). Install it by running:

Redis トランスポートは、ストリームを使用してメッセージをキューに入れます。このトランスポートには、Redis PHP 拡張機能 (>=4.3) と実行中の Redis サーバー (^5.0) が必要です。次を実行してインストールします。
1
$ composer require symfony/redis-messenger

The Redis transport DSN may looks like this:

Redis トランスポート DSN は次のようになります。
1
2
3
4
5
6
7
8
# .env
MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
# Full DSN Example
MESSENGER_TRANSPORT_DSN=redis://password@localhost:6379/messages/symfony/consumer?auto_setup=true&serializer=1&stream_max_entries=0&dbindex=0
# Redis Cluster Example
MESSENGER_TRANSPORT_DSN=redis://host-01:6379,redis://host-02:6379,redis://host-03:6379,redis://host-04:6379
# Unix Socket Example
MESSENGER_TRANSPORT_DSN=redis:///var/run/redis.sock

A number of options can be configured via the DSN or via the options key under the transport in messenger.yaml:

DSN を介して、またはmessenger.yaml の transport の下にある options キーを介して、多数のオプションを構成できます。
Option Description Default
stream The Redis stream name messages
group The Redis consumer group name symfony
consumer Consumer name used in Redis consumer
auto_setup Create the Redis group automatically? true
auth The Redis password  
delete_after_ack If true, messages are deleted automatically after processing them true
delete_after_reject If true, messages are deleted automatically if they are rejected true
lazy Connect only when a connection is really needed false
serializer How to serialize the final payload in Redis (the Redis::OPT_SERIALIZER option) Redis::SERIALIZER_PHP
stream_max_entries The maximum number of entries which the stream will be trimmed to. Set it to a large enough number to avoid losing pending messages 0 (which means "no trimming")
tls Enable TLS support for the connection false
redeliver_timeout Timeout before retrying a pending message which is owned by an abandoned consumer (if a worker died for some reason, this will occur, eventually you should retry the message) - in seconds. 3600
claim_interval Interval on which pending/abandoned messages should be checked for to claim - in milliseconds 60000 (1 Minute)
persistent_id String, if null connection is non-persistent. null
retry_interval Int, value in milliseconds 0
read_timeout Float, value in seconds default indicates unlimited 0
timeout Float, value in seconds default indicates unlimited 0
sentinel_master String, if null or empty Sentinel support is disabled null

6.1

6.1

The persistent_id, retry_interval, read_timeout, timeout, and sentinel_master options were introduced in Symfony 6.1.

persist_id、retry_interval、read_timeout、timeout、sentinel_master オプションは Symfony 6.1 で導入されました。

Caution

注意

There should never be more than one messenger:consume command running with the same combination of stream, group and consumer, or messages could end up being handled more than once. If you run multiple queue workers, consumer can be set to an environment variable, like %env(MESSENGER_CONSUMER_NAME)%, set by Supervisor (example below) or any other service used to manage the worker processes. In a container environment, the HOSTNAME can be used as the consumer name, since there is only one worker per container/host. If using Kubernetes to orchestrate the containers, consider using a StatefulSet to have stable names.

ストリーム、グループ、およびコンシューマーの同じ組み合わせで複数のメッセンジャー:消費コマンドを実行してはなりません。そうしないと、メッセージが複数回処理される可能性があります。複数のキュー ワーカーを実行する場合、%env(MESSENGER_CONSUMER_NAME)% などの環境変数にコンシューマーを設定できます。これは、Supervisor (以下の例) またはワーカー プロセスの管理に使用されるその他のサービスによって設定されます。コンテナー環境では、HOSTNAME をコンテナー/ホストごとにワーカーが 1 つしかないため、コンシューマー名として使用されます。 Kubernetes を使用してコンテナーを調整する場合は、StatefulSet を使用して安定した名前を付けることを検討してください。

Tip

ヒント

Set delete_after_ack to true (if you use a single group) or define stream_max_entries (if you can estimate how many max entries is acceptable in your case) to avoid memory leaks. Otherwise, all messages will remain forever in Redis.

delete_after_ack を true (単一のグループを使用する場合) に設定するか、definestream_max_entries (ケースで許容できる最大エントリ数を推定できる場合) に設定して、メモリ リークを回避します。そうしないと、すべてのメッセージが Redis に永久に残ります。

In Memory Transport

The in-memory transport does not actually deliver messages. Instead, it holds them in memory during the request, which can be useful for testing. For example, if you have an async_priority_normal transport, you could override it in the test environment to use this transport:

インメモリ トランスポートは、実際にはメッセージを配信しません。代わりに、リクエスト中にそれらをメモリに保持するため、テストに役立ちます。たとえば、async_priority_normal トランスポートがある場合、テスト環境でそれをオーバーライドして、このトランスポートを使用できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
# config/packages/test/messenger.yaml
framework:
    messenger:
        transports:
            async_priority_normal: 'in-memory://'

Then, while testing, messages will not be delivered to the real transport. Even better, in a test, you can check that exactly one message was sent during a request:

次に、テスト中、メッセージは実際のトランスポートに配信されません。さらに良いことに、テストでは、リクエスト中にメッセージが 1 つだけ送信されたことを確認できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// tests/Controller/DefaultControllerTest.php
namespace App\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Messenger\Transport\InMemoryTransport;

class DefaultControllerTest extends WebTestCase
{
    public function testSomething()
    {
        $client = static::createClient();
        // ...

        $this->assertSame(200, $client->getResponse()->getStatusCode());

        /* @var InMemoryTransport $transport */
        $transport = $this->getContainer()->get('messenger.transport.async_priority_normal');
        $this->assertCount(1, $transport->getSent());
    }
}

The transport has a number of options:

トランスポートにはいくつかのオプションがあります。
serialize (boolean, default: false)
Whether to serialize messages or not. This is useful to test an additional layer, especially when you use your own message serializer.
メッセージをシリアライズするかどうか。これは、特に独自のメッセージシリアライザーを使用する場合に、追加レイヤーをテストするのに役立ちます。

Note

ノート

All in-memory transports will be reset automatically after each test in test classes extending KernelTestCase or WebTestCase.

KernelTestCase または WebTestCase を拡張するクラスをテストするたびに、すべてのインメモリ トランスポートが自動的にリセットされます。

Amazon SQS

The Amazon SQS transport is perfect for applications hosted on AWS. Install it by running:

Amazon SQS トランスポートは、AWS でホストされるアプリケーションに最適です。次を実行してインストールします。
1
$ composer require symfony/amazon-sqs-messenger

The SQS transport DSN may looks like this:

SQS トランスポート DSN は次のようになります。
1
2
3
# .env
MESSENGER_TRANSPORT_DSN=https://sqs.eu-west-3.amazonaws.com/123456789012/messages?access_key=AKIAIOSFODNN7EXAMPLE&secret_key=j17M97ffSVoKI0briFoo9a
MESSENGER_TRANSPORT_DSN=sqs://localhost:9494/messages?sslmode=disable

Note

ノート

The transport will automatically create queues that are needed. This can be disabled by setting the auto_setup option to false.

トランスポートは、必要なキューを自動的に作成します。これは、auto_setup オプションを false に設定することで無効にできます。

Tip

ヒント

Before sending or receiving a message, Symfony needs to convert the queue name into an AWS queue URL by calling the GetQueueUrl API in AWS. This extra API call can be avoided by providing a DSN which is the queue URL.

メッセージを送受信する前に、Symfony は AWS で GetQueueUrl API を呼び出してキュー名を AWS キュー URL に変換する必要があります。この余分な API 呼び出しは、キュー URL である DSN を提供することで回避できます。

The transport has a number of options:

トランスポートにはいくつかのオプションがあります。
Option Description Default
access_key AWS access key must be urlencoded
account Identifier of the AWS account The owner of the credentials
auto_setup Whether the queue should be created automatically during send / get. true
buffer_size Number of messages to prefetch 9
debug If true it logs all HTTP requests and responses (it impacts performance) false
endpoint Absolute URL to the SQS service https://sqs.eu-west-1.amazonaws.com
poll_timeout Wait for new message duration in seconds 0.1
queue_name Name of the queue messages
region Name of the AWS region eu-west-1
secret_key AWS secret key must be urlencoded
session_token AWS session token  
visibility_timeout Amount of seconds the message will not be visible (Visibility Timeout) Queue's configuration
wait_time Long polling duration in seconds 20

6.1

6.1

The session_token option was introduced in Symfony 6.1.

session_token オプションは Symfony 6.1 で導入されました。

Note

ノート

The wait_time parameter defines the maximum duration Amazon SQS should wait until a message is available in a queue before sending a response. It helps reducing the cost of using Amazon SQS by eliminating the number of empty responses.

wait_time パラメータは、応答を送信する前にメッセージがキューで使用可能になるまで Amazon SQS が待機する最大時間を定義します。空の応答の数をなくすことで、Amazon SQS の使用コストを削減するのに役立ちます。

The poll_timeout parameter defines the duration the receiver should wait before returning null. It avoids blocking other receivers from being called.

poll_timeout パラメータは、null を返す前にレシーバが待機する時間を定義します。他の受信者が呼び出されるのをブロックすることを回避します。

Note

ノート

If the queue name is suffixed by .fifo, AWS will create a FIFO queue. Use the stamp AmazonSqsFifoStamp to define the Message group ID and the Message deduplication ID.

キュー名に .fifo というサフィックスが付いている場合、AWS は FIFO キューを作成します。スタンプ AmazonSqsFifoStamp を使用して、メッセージ グループ ID とメッセージ重複排除 ID を定義します。

FIFO queues don't support setting a delay per message, a value of delay: 0 is required in the retry strategy settings.

FIFO キューは、メッセージごとの遅延の設定をサポートしていません。遅延の値: 0 は、再試行戦略の設定で必要です。

Serializing Messages

When messages are sent to (and received from) a transport, they're serialized using PHP's native serialize() & unserialize() functions. You can change this globally (or for each transport) to a service that implements SerializerInterface:

メッセージがトランスポートに送信される (およびトランスポートから受信される) 場合、メッセージは PHP のネイティブの serialize() および unserialize() 関数を使用してシリアル化されます。これをグローバルに (またはトランスポートごとに)、SerializerInterface を実装するサービスに変更できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
# config/packages/messenger.yaml
framework:
    messenger:
        serializer:
            default_serializer: messenger.transport.symfony_serializer
            symfony_serializer:
                format: json
                context: { }

        transports:
            async_priority_normal:
                dsn: # ...
                serializer: messenger.transport.symfony_serializer

The messenger.transport.symfony_serializer is a built-in service that uses the Serializer component and can be configured in a few ways. If you do choose to use the Symfony serializer, you can control the context on a case-by-case basis via the SerializerStamp (see Envelopes & Stamps).

messenger.transport.symfony_serializer は、Serializer コンポーネントを使用する組み込みサービスであり、いくつかの方法で構成できます。Symfony シリアライザーを使用することを選択した場合は、SerializerStamp を介してケースバイケースでコンテキストを制御できます。 (封筒と切手参照)。

Tip

ヒント

When sending/receiving messages to/from another application, you may need more control over the serialization process. Using a custom serializer provides that control. See SymfonyCasts' message serializer tutorial for details.

別のアプリケーションとの間でメッセージを送受信する場合、シリアル化プロセスをさらに制御する必要がある場合があります。カスタム シリアライザーを使用すると、そのコントロールが提供されます。詳細については、SymfonyCasts のメッセージシリアライザーのチュートリアルを参照してください。

Customizing Handlers

Configuring Handlers Using Attributes

You can configure your handler by passing options to the attribute:

オプションを属性に渡すことで、ハンドラーを構成できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/MessageHandler/SmsNotificationHandler.php
namespace App\MessageHandler;

use App\Message\OtherSmsNotification;
use App\Message\SmsNotification;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler(fromTransport: 'async', priority: 10)]
class SmsNotificationHandler
{
    public function __invoke(SmsNotification $message)
    {
        // ...
    }
}

Possible options to configure with the attribute are:

属性で構成できるオプションは次のとおりです。
  • bus
    バス
  • fromTransport
    fromTransport
  • handles
    ハンドル
  • method
    方法
  • priority
    優先順位

Manually Configuring Handlers

Symfony will normally find and register your handler automatically. But, you can also configure a handler manually - and pass it some extra config - by tagging the handler service with messenger.message_handler

symfony は通常、ハンドラーを自動的に見つけて登録します。ただし、ハンドラーを手動で構成することもできます。また、ハンドラー サービスにmessenger.message_handlerのタグを付けて、追加の構成を渡すこともできます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
# config/services.yaml
services:
    App\MessageHandler\SmsNotificationHandler:
        tags: [messenger.message_handler]

        # or configure with options
        tags:
            -
                name: messenger.message_handler
                # only needed if can't be guessed by type-hint
                handles: App\Message\SmsNotification

Possible options to configure with tags are:

タグで設定できるオプションは次のとおりです。
  • bus
    バス
  • from_transport
    from_transport
  • handles
    ハンドル
  • method
    方法
  • priority
    優先順位

Handling Multiple Messages

A single handler class can handle multiple messages. For that add the #AsMessageHandler attribute to all the handling methods:

1 つのハンドラ クラスで複数のメッセージを処理できます。そのために、#AsMessageHandler 属性をすべての処理メソッドに追加します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/MessageHandler/SmsNotificationHandler.php
namespace App\MessageHandler;

use App\Message\OtherSmsNotification;
use App\Message\SmsNotification;

class SmsNotificationHandler
{
    #[AsMessageHandler]
    public function handleSmsNotification(SmsNotification $message)
    {
        // ...
    }

    #[AsMessageHandler]
    public function handleOtherSmsNotification(OtherSmsNotification $message)
    {
        // ...
    }
}

6.2

6.2

Implementing the MessageSubscriberInterface is another way to handle multiple messages with one handler class. This interface was deprecated in Symfony 6.2.

MessageSubscriberInterface の実装は、1 つのハンドラ クラスで複数のメッセージを処理するもう 1 つの方法です。このインターフェースは Symfony 6.2 で廃止されました。

Binding Handlers to Different Transports

Each message can have multiple handlers, and when a message is consumed all of its handlers are called. But you can also configure a handler to only be called when it's received from a specific transport. This allows you to have a single message where each handler is called by a different "worker" that's consuming a different transport.

各メッセージには複数のハンドラーを含めることができ、メッセージが消費されると、そのすべてのハンドラーが呼び出されます。ただし、特定のトランスポートから受信したときにのみ呼び出されるようにハンドラーを構成することもできます。これにより、異なるトランスポートを消費する異なる「ワーカー」によって各ハンドラーが呼び出される単一のメッセージを持つことができます。

Suppose you have an UploadedImage message with two handlers:

次の 2 つのハンドラーを持つ UploadedImage メッセージがあるとします。
  • ThumbnailUploadedImageHandler: you want this to be handled by a transport called image_transport
    ThumbnailUploadedImageHandler: これを image_transport というトランスポートで処理したい
  • NotifyAboutNewUploadedImageHandler: you want this to be handled by a transport called async_priority_normal
    NotifyAboutNewUploadedImageHandler: これを async_priority_normal というトランスポートで処理したい

To do this, add the from_transport option to each handler. For example:

これを行うには、from_transport オプションを各ハンドラーに追加します。例えば:
1
2
3
4
5
6
7
8
9
10
11
12
13
// src/MessageHandler/ThumbnailUploadedImageHandler.php
namespace App\MessageHandler;

use App\Message\UploadedImage;

#[AsMessageHandler(from_transport: 'image_transport')]
class ThumbnailUploadedImageHandler
{
    public function __invoke(UploadedImage $uploadedImage)
    {
        // do some thumbnailing
    }
}

And similarly:

同様に:
1
2
3
4
5
6
7
8
// src/MessageHandler/NotifyAboutNewUploadedImageHandler.php
// ...

#[AsMessageHandler(from_transport: 'async_priority_normal')]
class NotifyAboutNewUploadedImageHandler
{
    // ...
}

Then, make sure to "route" your message to both transports:

次に、メッセージを両方のトランスポートに「ルーティング」してください。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async_priority_normal: # ...
            image_transport: # ...

        routing:
            # ...
            'App\Message\UploadedImage': [image_transport, async_priority_normal]

That's it! You can now consume each transport:

それでおしまい!各トランスポートを使用できるようになりました。
1
2
3
4
# will only call ThumbnailUploadedImageHandler when handling the message
$ php bin/console messenger:consume image_transport -vv

$ php bin/console messenger:consume async_priority_normal -vv

Caution

注意

If a handler does not have from_transport config, it will be executed on every transport that the message is received from.

ハンドラーに from_transport 構成がない場合、メッセージが受信されるすべてのトランスポートで実行されます。

Extending Messenger

Envelopes & Stamps

A message can be any PHP object. Sometimes, you may need to configure something extra about the message - like the way it should be handled inside AMQP or adding a delay before the message should be handled. You can do that by adding a "stamp" to your message:

メッセージは任意の PHP オブジェクトにすることができます。 AMQP 内でメッセージを処理する方法や、メッセージを処理する前に遅延を追加する方法など、メッセージに関する追加の設定が必要になる場合があります。メッセージに「スタンプ」を追加することで、これを行うことができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;

public function index(MessageBusInterface $bus)
{
    $bus->dispatch(new SmsNotification('...'), [
        // wait 5 seconds before processing
        new DelayStamp(5000),
    ]);

    // or explicitly create an Envelope
    $bus->dispatch(new Envelope(new SmsNotification('...'), [
        new DelayStamp(5000),
    ]));

    // ...
}

Internally, each message is wrapped in an Envelope, which holds the message and stamps. You can create this manually or allow the message bus to do it. There are a variety of different stamps for different purposes and they're used internally to track information about a message - like the message bus that's handling it or if it's being retried after failure.

内部的には、各メッセージは、メッセージとスタンプを保持する Envelope にラップされます。これは手動で作成するか、メッセージ バスに任せることができます。さまざまな目的のためにさまざまなスタンプがあり、メッセージに関する情報を追跡するために内部的に使用されます。たとえば、失敗後に再試行されている場合に itor を処理するメッセージ バスなどです。

Middleware

What happens when you dispatch a message to a message bus depends on its collection of middleware and their order. By default, the middleware configured for each bus looks like this:

メッセージ バスにメッセージをディスパッチするとどうなるかは、ミドルウェアのコレクションとその順序によって異なります。デフォルトでは、各バス用に構成されたミドルウェアは次のようになります。
  1. add_bus_name_stamp_middleware - adds a stamp to record which bus this message was dispatched into;
    add_bus_name_stamp_middleware - このメッセージがディスパッチされたバスを記録するスタンプを追加します。
  2. dispatch_after_current_bus- see Transactional Messages: Handle New Messages After Handling is Done;
    dispatch_after_current_bus-「トランザクション メッセージ: 処理が完了した後に新しいメッセージを処理する」を参照してください。
  3. failed_message_processing_middleware - processes messages that are being retried via the failure transport to make them properly function as if they were being received from their original transport;
    failed_message_processing_middleware - 障害トランスポートを介して再試行されているメッセージを処理して、元のトランスポートから受信されたかのように適切に機能させます。
  4. Your own collection of middleware;
    ミドルウェアの独自のコレクション。
  5. send_message - if routing is configured for the transport, this sends messages to that transport and stops the middleware chain;
    send_message - トランスポート用にルーティングが構成されている場合、これはメッセージをそのトランスポートに送信し、ミドルウェア チェーンを停止します。
  6. handle_message - calls the message handler(s) for the given message.
    handle_message - 指定されたメッセージのメッセージ ハンドラを呼び出します。

Note

ノート

These middleware names are actually shortcut names. The real service ids are prefixed with messenger.middleware. (e.g. messenger.middleware.handle_message).

これらのミドルウェア名は、実際にはショートカット名です。実際のサービス ID には、messenger.middleware というプレフィックスが付きます。 (例:messenger.middleware.handle_message)。

The middleware are executed when the message is dispatched but also again when a message is received via the worker (for messages that were sent to a transport to be handled asynchronously). Keep this in mind if you create your own middleware.

ミドルウェアは、メッセージがディスパッチされたときに実行されますが、ワーカーを介してメッセージが受信されたときにも実行されます (トランスポートに送信されたメッセージは非同期で処理されます)。独自のミドルウェアを作成する場合は、このことに注意してください。

You can add your own middleware to this list, or completely disable the default middleware and only include your own:

独自のミドルウェアをこのリストに追加するか、デフォルトのミドルウェアを完全に無効にして独自のもののみを含めることができます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
# config/packages/messenger.yaml
framework:
    messenger:
        buses:
            messenger.bus.default:
                # disable the default middleware
                default_middleware: false

                # and/or add your own
                middleware:
                    # service ids that implement Symfony\Component\Messenger\Middleware\MiddlewareInterface
                    - 'App\Middleware\MyMiddleware'
                    - 'App\Middleware\AnotherMiddleware'

Note

ノート

If a middleware service is abstract, a different instance of the service will be created per bus.

ミドルウェア サービスが抽象的である場合、サービスの異なるインスタンスがバスごとに作成されます。

Middleware for Doctrine

1.11

1.11

The following Doctrine middleware was introduced in DoctrineBundle 1.11.

以下の Doctrine ミドルウェアは DoctrineBundle 1.11 で導入されました。

If you use Doctrine in your app, a number of optional middleware exist that you may want to use:

アプリでDoctrineを使用する場合、使用したいオプションのミドルウェアがいくつかあります:
  • YAML
    YAML
  • XML
    XML
  • PHP
    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
# config/packages/messenger.yaml
framework:
    messenger:
        buses:
            command_bus:
                middleware:
                    # each time a message is handled, the Doctrine connection
                    # is "pinged" and reconnected if it's closed. Useful
                    # if your workers run for a long time and the database
                    # connection is sometimes lost
                    - doctrine_ping_connection

                    # After handling, the Doctrine connection is closed,
                    # which can free up database connections in a worker,
                    # instead of keeping them open forever
                    - doctrine_close_connection

                    # wraps all handlers in a single Doctrine transaction
                    # handlers do not need to call flush() and an error
                    # in any handler will cause a rollback
                    - doctrine_transaction

                    # or pass a different entity manager to any
                    #- doctrine_transaction: ['custom']

Other Middlewares

Add the router_context middleware if you need to generate absolute URLs in the consumer (e.g. render a template with links). This middleware stores the original request context (i.e. the host, the HTTP port, etc.) which is needed when building absolute URLs.

コンシューマーで絶対 URL を生成する必要がある場合は、router_context ミドルウェアを追加します (たとえば、リンクを含むテンプレートをレンダリングします)。このミドルウェアは、絶対 URL を構築するときに必要な元の要求コンテキスト (つまり、ホスト、HTTP ポートなど) を保存します。

Add the validation middleware if you need to validate the message object using the Validator component before handling it. If validation fails, a ValidationFailedException will be thrown. The ValidationStamp can be used to configure the validation groups.

処理する前に Validator コンポーネントを使用して messageobject を検証する必要がある場合は、検証ミドルウェアを追加します。検証が失敗すると、ValidationFailedException がスローされます。 ValidationStamp を使用して、検証グループを構成できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
# config/packages/messenger.yaml
framework:
    messenger:
        buses:
            command_bus:
                middleware:
                    - router_context
                    - validation

Messenger Events

In addition to middleware, Messenger also dispatches several events. You can create an event listener to hook into various parts of the process. For each, the event class is the event name:

ミドルウェアに加えて、Messenger はいくつかのイベントもディスパッチします。プロセスのさまざまな部分にフックするイベント リスナーを作成できます。それぞれのイベント クラスはイベント名です。

Multiple Buses, Command & Event Buses

Messenger gives you a single message bus service by default. But, you can configure as many as you want, creating "command", "query" or "event" buses and controlling their middleware. See Multiple Buses.

Messenger は、デフォルトで単一のメッセージ バス サービスを提供します。ただし、「コマンド」、「クエリ」、または「イベント」バスを作成し、それらのミドルウェアを制御して、必要なだけ構成できます。複数のバスを参照してください。

Learn more