summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/third_party/websockets/docs/howto/supervisor.rst
blob: 5eefc7711b80a8bdd5865f6a3443a352d19e7b89 (plain)
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
Deploy with Supervisor
======================

This guide proposes a simple way to deploy a websockets server directly on a
Linux or BSD operating system.

We'll configure Supervisor_ to run several server processes and to restart
them if needed.

.. _Supervisor: http://supervisord.org/

We'll bind all servers to the same port. The OS will take care of balancing
connections.

Create and activate a virtualenv:

.. code-block:: console

    $ python -m venv supervisor-websockets
    $ . supervisor-websockets/bin/activate

Install websockets and Supervisor:

.. code-block:: console

    $ pip install websockets
    $ pip install supervisor

Save this app to a file called ``app.py``:

.. literalinclude:: ../../example/deployment/supervisor/app.py

This is an echo server with two features added for the purpose of this guide:

* It shuts down gracefully when receiving a ``SIGTERM`` signal;
* It enables the ``reuse_port`` option of :meth:`~asyncio.loop.create_server`,
  which in turns sets ``SO_REUSEPORT`` on the accept socket.

Save this Supervisor configuration to ``supervisord.conf``:

.. literalinclude:: ../../example/deployment/supervisor/supervisord.conf

This is the minimal configuration required to keep four instances of the app
running, restarting them if they exit.

Now start Supervisor in the foreground:

.. code-block:: console

    $ supervisord -c supervisord.conf -n
    INFO Increased RLIMIT_NOFILE limit to 1024
    INFO supervisord started with pid 43596
    INFO spawned: 'websockets-test_00' with pid 43597
    INFO spawned: 'websockets-test_01' with pid 43598
    INFO spawned: 'websockets-test_02' with pid 43599
    INFO spawned: 'websockets-test_03' with pid 43600
    INFO success: websockets-test_00 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    INFO success: websockets-test_01 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    INFO success: websockets-test_02 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    INFO success: websockets-test_03 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

In another shell, after activating the virtualenv, we can connect to the app —
press Ctrl-D to exit:

.. code-block:: console

    $ python -m websockets ws://localhost:8080/
    Connected to ws://localhost:8080/.
    > Hello!
    < Hello!
    Connection closed: 1000 (OK).

Look at the pid of an instance of the app in the logs and terminate it:

.. code-block:: console

    $ kill -TERM 43597

The logs show that Supervisor restarted this instance:

.. code-block:: console

    INFO exited: websockets-test_00 (exit status 0; expected)
    INFO spawned: 'websockets-test_00' with pid 43629
    INFO success: websockets-test_00 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

Now let's check what happens when we shut down Supervisor, but first let's
establish a connection and leave it open:

.. code-block:: console

    $ python -m websockets ws://localhost:8080/
    Connected to ws://localhost:8080/.
    >

Look at the pid of supervisord itself in the logs and terminate it:

.. code-block:: console

    $ kill -TERM 43596

The logs show that Supervisor terminated all instances of the app before
exiting:

.. code-block:: console

    WARN received SIGTERM indicating exit request
    INFO waiting for websockets-test_00, websockets-test_01, websockets-test_02, websockets-test_03 to die
    INFO stopped: websockets-test_02 (exit status 0)
    INFO stopped: websockets-test_03 (exit status 0)
    INFO stopped: websockets-test_01 (exit status 0)
    INFO stopped: websockets-test_00 (exit status 0)

And you can see that the connection to the app was closed gracefully:

.. code-block:: console

    $ python -m websockets ws://localhost:8080/
    Connected to ws://localhost:8080/.
    Connection closed: 1001 (going away).

In this example, we've been sharing the same virtualenv for supervisor and
websockets.

In a real deployment, you would likely:

* Install Supervisor with the package manager of the OS.
* Create a virtualenv dedicated to your application.
* Add ``environment=PATH="path/to/your/virtualenv/bin"`` in the Supervisor
  configuration. Then ``python app.py`` runs in that virtualenv.