@@ -486,23 +486,30 @@ Deploying to Production
486
486
487
487
On production, there are a few important things to think about:
488
488
489
- **Use Supervisor to keep your worker(s) running **
489
+ **Use a Process Manager like Supervisor or systemd to keep your worker(s) running **
490
490
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 >`.
492
493
493
494
**Don't Let Workers Run Forever **
494
495
Some services (like Doctrine's ``EntityManager ``) will consume more memory
495
496
over time. So, instead of allowing your worker to run forever, use a flag
496
497
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
498
499
are also other options like ``--memory-limit=128M `` and ``--time-limit=3600 ``.
499
500
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
+
500
507
**Restart Workers on Deploy **
501
508
Each time you deploy, you'll need to restart all your worker processes so
502
509
that they see the newly deployed code. To do this, run ``messenger:stop-workers ``
503
510
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 >`
506
513
cache internally - so make sure this is configured to use an adapter you like.
507
514
508
515
**Use the Same Cache Between Deploys **
@@ -695,8 +702,49 @@ Next, tell Supervisor to read your config and start your workers:
695
702
696
703
See the `Supervisor docs `_ for more details.
697
704
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
+
698
746
Graceful Shutdown
699
- ~~~~~~~~~~~~~~~~~
747
+ .................
700
748
701
749
If you install the `PCNTL `_ PHP extension in your project, workers will handle
702
750
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:
712
760
[program:x]
713
761
stopwaitsecs =20
714
762
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
+
715
845
Stateless Worker
716
846
~~~~~~~~~~~~~~~~
717
847
@@ -2190,6 +2320,8 @@ middleware and *only* include your own:
2190
2320
If a middleware service is abstract, a different instance of the service will
2191
2321
be created per bus.
2192
2322
2323
+ .. _middleware-doctrine :
2324
+
2193
2325
Middleware for Doctrine
2194
2326
~~~~~~~~~~~~~~~~~~~~~~~
2195
2327
@@ -2377,6 +2509,7 @@ Learn more
2377
2509
.. _`streams` : https://redis.io/topics/streams-intro
2378
2510
.. _`Supervisor docs` : http://supervisord.org/
2379
2511
.. _`PCNTL` : https://www.php.net/manual/book.pcntl.php
2512
+ .. _`systemd docs` : https://www.freedesktop.org/wiki/Software/systemd/
2380
2513
.. _`SymfonyCasts' message serializer tutorial` : https://symfonycasts.com/screencast/messenger/transport-serializer
2381
2514
.. _`Long polling` : https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-short-and-long-polling.html
2382
2515
.. _`Visibility Timeout` : https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html
0 commit comments