Skip to content

Commit 09fd70c

Browse files
tvlooywouterj
authored andcommitted
Messenger process managers
Add information about mysql timeouts, supervisor FATAL, systemd user services. Add docs for #13617
1 parent 78452d9 commit 09fd70c

File tree

1 file changed

+139
-6
lines changed

1 file changed

+139
-6
lines changed

messenger.rst

Lines changed: 139 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -486,23 +486,30 @@ Deploying to Production
486486

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

489-
**Use Supervisor to keep your worker(s) running**
489+
**Use a Process Manager like Supervisor or systemd to keep your worker(s) running**
490490
You'll want one or more "workers" running at all times. To do that, use a
491-
process control system like :ref:`Supervisor <messenger-supervisor>`.
491+
process control system like :ref:`Supervisor <messenger-supervisor>`
492+
or :ref:`systemd <messenger-systemd>`.
492493

493494
**Don't Let Workers Run Forever**
494495
Some services (like Doctrine's ``EntityManager``) will consume more memory
495496
over time. So, instead of allowing your worker to run forever, use a flag
496497
like ``messenger:consume --limit=10`` to tell your worker to only handle 10
497-
messages before exiting (then Supervisor will create a new process). There
498+
messages before exiting (then the process manager will create a new process). There
498499
are also other options like ``--memory-limit=128M`` and ``--time-limit=3600``.
499500

501+
**Stopping Workers That Encounter Errors**
502+
If a worker dependency like your database server is down, or timeout is reached,
503+
you can try to add :ref:`reconnect logic <middleware-doctrine>`, or just quit
504+
the worker if it receives too many errors with the ``--failure-limit`` option of
505+
the ``messenger:consume`` command.
506+
500507
**Restart Workers on Deploy**
501508
Each time you deploy, you'll need to restart all your worker processes so
502509
that they see the newly deployed code. To do this, run ``messenger:stop-workers``
503510
on deployment. This will signal to each worker that it should finish the message
504-
it's currently handling and should shut down gracefully. Then, Supervisor will create
505-
new worker processes. The command uses the :ref:`app <cache-configuration-with-frameworkbundle>`
511+
it's currently handling and should shut down gracefully. Then, the process manager
512+
will create new worker processes. The command uses the :ref:`app <cache-configuration-with-frameworkbundle>`
506513
cache internally - so make sure this is configured to use an adapter you like.
507514

508515
**Use the Same Cache Between Deploys**
@@ -695,8 +702,49 @@ Next, tell Supervisor to read your config and start your workers:
695702
696703
See the `Supervisor docs`_ for more details.
697704

705+
It is possible to end up in a situation where the supervisor job gets into a
706+
FATAL (too many start retries) state when trying to restart when something is
707+
not yet available. You can prevent this by wrapping the Symfony script with a
708+
shell script and sleep when the script fails:
709+
710+
.. code-block:: bash
711+
712+
#!/usr/bin/env bash
713+
714+
# Supervisor sends TERM to services when stopped.
715+
# This wrapper has to pass the signal to it's child.
716+
# Note that we send TERM (graceful) instead of KILL (immediate).
717+
_term() {
718+
echo "[GOT TERM, SIGNALING CHILD]"
719+
kill -TERM "$child" 2>/dev/null
720+
exit 1
721+
}
722+
723+
trap _term SIGTERM
724+
725+
# Execute console.php with whatever arguments were specified to this script
726+
"$@" &
727+
child=$!
728+
wait "$child"
729+
rc=$?
730+
731+
# Delay to prevent supervisor from restarting too fast on failure
732+
sleep 30
733+
734+
# Return with the exit code of the wrapped process
735+
exit $rc
736+
737+
The supervisor job would then look like this:
738+
739+
.. code-block:: ini
740+
741+
;/etc/supervisor/conf.d/messenger-worker.conf
742+
[program:messenger-consume]
743+
command=/path/to/your/app/bin/console_wrapper php /path/to/your/app/bin/console messenger:consume async --time-limit=3600"
744+
...
745+
698746
Graceful Shutdown
699-
~~~~~~~~~~~~~~~~~
747+
.................
700748

701749
If you install the `PCNTL`_ PHP extension in your project, workers will handle
702750
the ``SIGTERM`` POSIX signal to finish processing their current message before
@@ -712,6 +760,88 @@ of the desired grace period in seconds) in order to perform a graceful shutdown:
712760
[program:x]
713761
stopwaitsecs=20
714762
763+
.. _messenger-systemd:
764+
765+
Systemd Configuration
766+
~~~~~~~~~~~~~~~~~~~~~
767+
768+
While Supervisor is a great tool, it has the disadvantage that you need system
769+
access to run it. Systemd has become the standard on most Linux distributions,
770+
and has a good alternative called *user services*.
771+
772+
Systemd user service configuration files typically live in a ``~/.config/systemd/user``
773+
directory. For example, you can create a new ``messenger-worker.service`` file. Or a
774+
``messenger-worker@.service`` file if you want more instances running at the same time:
775+
776+
.. code-block:: ini
777+
778+
[Unit]
779+
Description=Symfony messenger-consume %i
780+
781+
[Service]
782+
ExecStart=php /path/to/your/app/bin/console messenger:consume async --time-limit=3600
783+
Restart=always
784+
RestartSec=30
785+
786+
[Install]
787+
WantedBy=default.target
788+
789+
Now, tell systemd to enable and start one worker:
790+
791+
.. code-block:: terminal
792+
793+
$ systemctl --user enable messenger-worker@1.service
794+
$ systemctl --user start messenger-worker@1.service
795+
796+
# to enable and start 20 workers
797+
$ systemctl --user enable messenger-worker@{1..20}.service
798+
$ systemctl --user start messenger-worker@{1..20}.service
799+
800+
If you change your service config file, you need to reload the daemon:
801+
802+
.. code-block:: terminal
803+
804+
$ systemctl --user daemon-reload
805+
806+
To restart all your consumers:
807+
808+
.. code-block:: terminal
809+
810+
$ systemctl --user restart messenger-consume@*.service
811+
812+
The systemd user instance is only started after the first login of the
813+
particular user. Consumer often need to start on system boot instead.
814+
Enable lingering on the user to activate that behavior:
815+
816+
.. code-block:: terminal
817+
818+
$ loginctl enable-linger <your-username>
819+
820+
Logs are managed by journald and can be worked with using the journalctl
821+
command:
822+
823+
.. code-block:: terminal
824+
825+
# follow logs of consumer nr 11
826+
$ journalctl -f --user-unit messenger-consume@11.service
827+
828+
# follow logs of all consumers
829+
$ journalctl -f --user-unit messenger-consume@*
830+
831+
# follow all logs from your user services
832+
$ journalctl -f _UID=$UID
833+
834+
See the `systemd docs`_ for more details.
835+
836+
.. note::
837+
838+
You either need elevated privileges for the ``journalctl`` command, or add
839+
your user to the systemd-journal group:
840+
841+
.. code-block:: terminal
842+
843+
$ sudo usermod -a -G systemd-journal <your-username>
844+
715845
Stateless Worker
716846
~~~~~~~~~~~~~~~~
717847

@@ -2190,6 +2320,8 @@ middleware and *only* include your own:
21902320
If a middleware service is abstract, a different instance of the service will
21912321
be created per bus.
21922322

2323+
.. _middleware-doctrine:
2324+
21932325
Middleware for Doctrine
21942326
~~~~~~~~~~~~~~~~~~~~~~~
21952327

@@ -2377,6 +2509,7 @@ Learn more
23772509
.. _`streams`: https://redis.io/topics/streams-intro
23782510
.. _`Supervisor docs`: http://supervisord.org/
23792511
.. _`PCNTL`: https://www.php.net/manual/book.pcntl.php
2512+
.. _`systemd docs`: https://www.freedesktop.org/wiki/Software/systemd/
23802513
.. _`SymfonyCasts' message serializer tutorial`: https://symfonycasts.com/screencast/messenger/transport-serializer
23812514
.. _`Long polling`: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html
23822515
.. _`Visibility Timeout`: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html

0 commit comments

Comments
 (0)