Deployment¶
Application server¶
The author of websockets
isn’t aware of best practices for deploying
network services based on asyncio
, let alone application servers.
You can run a script similar to the server example, inside a supervisor if you deem that useful.
You can also add a wrapper to daemonize the process. Third-party libraries provide solutions for that.
If you can share knowledge on this topic, please file an issue. Thanks!
Graceful shutdown¶
You may want to close connections gracefully when shutting down the server,
perhaps after executing some cleanup logic. There are two ways to achieve this
with the object returned by serve()
:
using it as a asynchronous context manager, or
calling its
close()
method, then waiting for itswait_closed()
method to complete.
Tasks that handle connections will be cancelled. For example, if the handler
is awaiting recv()
, that call will
raise CancelledError
.
On Unix systems, shutdown is usually triggered by sending a signal.
Here’s a full example (Unix-only):
#!/usr/bin/env python
import asyncio
import signal
import websockets
async def echo(websocket, path):
async for message in websocket:
await websocket.send(message)
async def echo_server(stop):
async with websockets.serve(echo, 'localhost', 8765):
await stop
loop = asyncio.get_event_loop()
# The stop condition is set when receiving SIGTERM.
stop = asyncio.Future()
loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)
# Run the server until the stop condition is met.
loop.run_until_complete(echo_server(stop))
async
and await
were introduced in Python 3.5. websockets supports
asynchronous context managers on Python ≥ 3.5.1. async for
was introduced
in Python 3.6. Here’s the equivalent for older Python versions:
#!/usr/bin/env python
import asyncio
import signal
import websockets
async def echo(websocket, path):
while True:
try:
msg = await websocket.recv()
except websockets.ConnectionClosed:
break
else:
await websocket.send(msg)
loop = asyncio.get_event_loop()
# Create the server.
start_server = websockets.serve(echo, 'localhost', 8765)
server = loop.run_until_complete(start_server)
# Run the server until receiving SIGTERM.
stop = asyncio.Future()
loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)
loop.run_until_complete(stop)
# Shut down the server.
server.close()
loop.run_until_complete(server.wait_closed())
It’s more difficult to achieve the same effect on Windows. Some third-party projects try to help with this problem.
If your server doesn’t run in the main thread, look at
call_soon_threadsafe()
.
Memory use¶
In order to avoid excessive memory use caused by buffer bloat, it is strongly recommended to tune buffer sizes.
Most importantly max_size
should be lowered according to the expected size
of messages. It is also suggested to lower max_queue
, read_limit
and
write_limit
if memory use is a concern.
Port sharing¶
The WebSocket protocol is an extension of HTTP/1.1. It can be tempting to serve both HTTP and WebSocket on the same port.
The author of websockets
doesn’t think that’s a good idea, due to the
widely different operational characteristics of HTTP and WebSocket.
websockets
provide minimal support for responding to HTTP requests with
the process_request()
hook. Typical
use cases include health checks. Here’s an example:
#!/usr/bin/env python
# WS echo server with HTTP endpoint at /health/
import asyncio
import http
import websockets
class ServerProtocol(websockets.WebSocketServerProtocol):
async def process_request(self, path, request_headers):
if path == '/health/':
return http.HTTPStatus.OK, [], b'OK\n'
async def echo(websocket, path):
async for message in websocket:
await websocket.send(message)
start_server = websockets.serve(
echo, 'localhost', 8765, create_protocol=ServerProtocol)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()