Update deployment docs

This commit is contained in:
Andrew Godwin 2016-02-21 13:06:33 +00:00
parent 973b8b72ad
commit 3b8feb5b96
2 changed files with 46 additions and 39 deletions

View File

@ -34,19 +34,22 @@ serve as the communication layer - for example, the Redis backend connects
to a Redis server. All this goes into the ``CHANNEL_BACKENDS`` setting; to a Redis server. All this goes into the ``CHANNEL_BACKENDS`` setting;
here's an example for a remote Redis server:: here's an example for a remote Redis server::
CHANNEL_BACKENDS = { CHANNEL_LAYERS = {
"default": { "default": {
"BACKEND": "channels.backends.redis_py.RedisChannelBackend", "BACKEND": "asgi_redis.RedisChannelLayer",
"HOSTS": [("redis-channel", 6379)], "CONFIG": {
"hosts": [("redis-server-name", 6379)],
},
"ROUTING": "my_project.routing.channel_routing",
}, },
} }
To use the Redis backend you have to install the redis package:: To use the Redis backend you have to install it::
pip install -U redis pip install -U asgi_redis
Make sure the same setting file is used across all your workers, interfaces Make sure the same settings file is used across all your workers, interfaces
and WSGI apps; without it, they won't be able to talk to each other and things and WSGI apps; without it, they won't be able to talk to each other and things
will just fail to work. will just fail to work.
@ -88,44 +91,44 @@ do the work of taking incoming requests and loading them into the channels
system. system.
You can just keep running your Django code as a WSGI app if you like, behind You can just keep running your Django code as a WSGI app if you like, behind
something like uwsgi or gunicorn, and just use the WSGI interface as the app something like uwsgi or gunicorn; this won't let you support WebSockets, though.
you load into the server - just set it to use Still, if you want to use a WSGI server and have it talk to a worker server
``channels.interfaces.wsgi:WSGIHandler``. cluster on the backend, see :ref:`wsgi-to-asgi`.
If you want to support WebSockets, however, you'll need to run another If you want to support WebSockets, long-poll HTTP requests and other Channels
interface server, as the WSGI protocol has no support for WebSockets. features, you'll need to run a native ASGI interface server, as the WSGI
Channels ships with an Autobahn-based WebSocket interface server specification has no support for running these kinds of requests concurrenctly.
that should suit your needs; however, you could also use a third-party Channels ships with an interface server that we recommend you use called
interface server or write one yourself, as long as it follows the *Daphne*; it supports WebSockets, long-poll HTTP requests, HTTP/2 *(soon)*
:doc:`message-standards`. and performs quite well. Of course, any ASGI-compliant server will work!
Notably, it's possible to combine more than one protocol into the same Notably, Daphne has a nice feature where it supports all of these protocols on
interface server, and the one Channels ships with does just this; it can the same port and on all paths; it auto-negotiates between HTTP and WebSocket,
optionally serve HTTP requests as well as WebSockets, though by default so there's no need to have your WebSockets on a separate port or path (and
it will just serve WebSockets and assume you're routing requests to the right they'll be able to share cookies with your normal view code).
kind of server using your load balancer or reverse proxy.
To run a normal WebSocket server, just run:: To run Daphne, it just needs to be supplied with a channel backend;
first, make sure your project has an ``asgi.py`` file that looks like this
(it should live next to ``wsgi.py``)::
python manage.py runwsserver import os
from channels.asgi import get_channel_layer
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_project.settings")
channel_layer = get_channel_layer()
Then, you can run Daphne and supply the channel layer as the argument:
daphne my_project.asgi:channel_layer
Like ``runworker``, you should place this inside an init system or something Like ``runworker``, you should place this inside an init system or something
like supervisord to ensure it is re-run if it exits unexpectedly. like supervisord to ensure it is re-run if it exits unexpectedly.
If you want to enable serving of normal HTTP requests as well, just run:: If you only run Daphne and no workers, all of your page requests will seem to
hang forever; that's because Daphne doesn't have any worker servers to handle
python manage.py runwsserver --accept-all the request and it's waiting for one to appear (while ``runserver`` also uses
Daphne, it launches a worker thread along with it in the same process).
This interface server is built on in-process asynchronous solutions
(Twisted for Python 2, and asyncio for Python 3) and so should be able to
handle a lot of simultaneous connections. That said, you should still plan to
run a cluster of them and load-balance between them; the per-connection memory
overhead is moderately high.
Finally, note that it's entirely possible for interface servers to be written
in a language other than Python, though this would mean they could not take
advantage of the channel backend abstraction code and so they'd likely be
custom-written for a single channel backend.
Deploying new versions of code Deploying new versions of code
@ -145,3 +148,7 @@ There's no need to restart the WSGI or WebSocket interface servers unless
you've upgraded your version of Channels or changed any settings; you've upgraded your version of Channels or changed any settings;
none of your code is used by them, and all middleware and code that can none of your code is used by them, and all middleware and code that can
customise requests is run on the consumers. customise requests is run on the consumers.
You can even use different Python versions for the interface servers and the
workers; the ASGI protocol that channel layers communicate over
is designed to be very portable and network-transparent.

View File

@ -332,7 +332,8 @@ store it in the session; thankfully, Channels ships with both a ``channel_sessio
decorator that works like the ``http_session_user`` decorator we mentioned above but decorator that works like the ``http_session_user`` decorator we mentioned above but
loads the user from the *channel* session rather than the *HTTP* session, loads the user from the *channel* session rather than the *HTTP* session,
and a function called ``transfer_user`` which replicates a user from one session and a function called ``transfer_user`` which replicates a user from one session
to another. to another. Even better, it combines all of these into a ``channel_session_user_from_http``
decorator.
Bringing that all together, let's make a chat server where users can only Bringing that all together, let's make a chat server where users can only
chat to people with the same first letter of their username:: chat to people with the same first letter of their username::
@ -343,8 +344,7 @@ chat to people with the same first letter of their username::
from channels.auth import http_session_user, channel_session_user, transfer_user from channels.auth import http_session_user, channel_session_user, transfer_user
# Connected to websocket.connect # Connected to websocket.connect
@channel_session @channel_session_user_from_http
@http_session_user
def ws_add(message): def ws_add(message):
# Copy user from HTTP to channel session # Copy user from HTTP to channel session
transfer_user(message.http_session, message.channel_session) transfer_user(message.http_session, message.channel_session)