diff --git a/docs/asgi.rst b/docs/asgi.rst index 0b19fc7..da53c1e 100644 --- a/docs/asgi.rst +++ b/docs/asgi.rst @@ -6,18 +6,6 @@ Draft ASGI Spec considered draft yet. Even the name might change; this is being written as development progresses.** -:: - - PEP: XXX - Title: ASGI (Asynchronous Server Gateway Interface) - Version: $Revision$ - Last-Modified: $Date$ - Author: Andrew Godwin - Status: Draft - Type: Informational - Content-Type: text/x-rst - Created: ? - Post-History: ? Abstract ======== diff --git a/docs/backends.rst b/docs/backends.rst index e132654..08a40c1 100644 --- a/docs/backends.rst +++ b/docs/backends.rst @@ -1,28 +1,34 @@ -Backends -======== +Channel Layer Types +=================== Multiple choices of backend are available, to fill different tradeoffs of complexity, throughput and scalability. You can also write your own backend if -you wish; the API is very simple and documented below. +you wish; the spec they confirm to is called :doc:`ASGI `. Any +ASGI-compliant channel layer can be used. Redis ----- -The Redis backend is the recommended backend to run Channels with, as it +The Redis layer is the recommended backend to run Channels with, as it supports both high throughput on a single Redis server as well as the ability to run against a set of Redis servers in a sharded mode. -To use the Redis backend you have to install the redis package:: +To use the Redis layer, simply install it from PyPI (it lives in a separate +package, as we didn't want to force a dependency on the redis-py for the main +install): - pip install -U redis + pip install -U asgi_redis By default, it will attempt to connect to a Redis server on ``localhost:6379``, -but you can override this with the ``HOSTS`` setting:: +but you can override this with the ``hosts`` key in its config:: - CHANNEL_BACKENDS = { + CHANNEL_LAYERS = { "default": { - "BACKEND": "channels.backends.redis_py.RedisChannelBackend", - "HOSTS": [("redis-channel-1", 6379), ("redis-channel-2", 6379)], + "BACKEND": "asgi_redis.RedisChannelLayer", + "ROUTING": "???", + "CONFIG": { + "hosts": [("redis-channel-1", 6379), ("redis-channel-2", 6379)], + }, }, } @@ -49,72 +55,38 @@ settings. Any misconfigured interface server or worker will drop some or all messages. -In-memory ---------- - -The in-memory backend is the simplest, and not really a backend as such; -it exists purely to enable Django to run in a "normal" mode where no Channels -functionality is available, just normal HTTP request processing. You should -never need to set it explicitly. - -This backend provides no network transparency or non-blocking guarantees. - Database -------- -======= -Writing Custom Backends ------------------------ +The database layer is intended as a short-term solution for people who can't +use a more production-ready layer (for example, Redis), but still want something +that will work cross-process. It has poor performance, and is only +recommended for development or extremely small deployments. -Backend Requirements -^^^^^^^^^^^^^^^^^^^^ +This layer is included with Channels; just set your ``BACKEND`` to +``channels.backends.database.DatabaseChannelLayer``, and it will use the +default Django database alias to store messages. You can change the alias +by setting ``CONFIG`` to ``{'alias': 'aliasname'}``. -While the channel backends are pluggable, there's a minimum standard they -must meet in terms of the way they perform. -In particular, a channel backend MUST: +In-memory +--------- -* Provide a ``send()`` method which sends a message onto a named channel +The in-memory layer is purely an implementation detail used when running +the entire Django stack in a single process; the most common case of this +is ``runserver``, where a server thread, channel layer, and worker thread all +co-exist inside the same python process. -* Provide a ``receive_many()`` method which returns an available message on the - provided channels, or returns no message either instantly or after a short - delay (it must not block indefinitely) +You should not need to use this process manually, but if you want to, +it's available from ``asgiref.inmemory.ChannelLayer``. -* Provide a ``group_add()`` method which adds a channel to a named group - for at least the provided expiry period. -* Provide a ``group_discard()`` method with removes a channel from a named - group if it was added, and nothing otherwise. +Writing Custom Channel Layers +----------------------------- -* Provide a ``group_members()`` method which returns an iterable of all - channels currently in the group. - -* Preserve the ordering of messages inside a channel - -* Never deliver a message more than once (design for at-most-once delivery) - -* Never block on sending of a message (dropping the message/erroring is preferable to blocking) - -* Be able to store messages of at least 5MB in size - -* Allow for channel and group names of up to 200 printable ASCII characters - -* Expire messages only after the expiry period provided is up (a backend may - keep them longer if it wishes, but should expire them at some reasonable - point to ensure users do not come to rely on permanent messages) - -In addition, it SHOULD: - -* Provide a ``receive_many_blocking()`` method which is like ``receive_many()`` - but blocks until a message is available. - -* Provide a ``send_group()`` method which sends a message to every channel - in a group. - -* Make ``send_group()`` perform better than ``O(n)``, where ``n`` is the - number of members in the group; preferably send the messages to all - members in a single call to your backing datastore or protocol. - -* Try and preserve a rough global ordering, so that one busy channel does not - drown out an old message in another channel if a worker is listening on both. +The interface channel layers present to Django and other software that +communicates over them is codified in a specification called :doc:`ASGI `. +Any channel layer that conforms to the :doc:`ASGI spec ` can be used +by Django; just set ``BACKEND`` to the class to instantiate and ``CONFIG`` to +a dict of keyword arguments to initialize the class with. diff --git a/docs/index.rst b/docs/index.rst index a7a5e6c..c77d205 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,7 +27,6 @@ Contents: getting-started deploying integration-changes - message-standards scaling backends integration-plan diff --git a/docs/message-standards.rst b/docs/message-standards.rst deleted file mode 100644 index c1e6108..0000000 --- a/docs/message-standards.rst +++ /dev/null @@ -1,133 +0,0 @@ -Message Standards -================= - -Some standardised message formats are used for common message types - they -are detailed below. Message formats are meant to be generic and offload as -much protocol-specific processing to the interface server as is reasonable; -thus, they should generally represent things at as high a level as makes sense. - -In addition to the standards outlined below, each message may contain a -``reply_channel``, which details where to send responses. Protocols with -separate connection and data receiving messages (like WebSockets) will only -contain the connection and detailed client information in the first message; -use the ``@channel_session`` decorator to persist this data to consumers of -the received data (or something else based on ``reply_channel``). - -All messages must be able to be encoded as JSON; channel backends don't -necessarily have to use JSON, but we consider it the lowest common denominator -for serialisation format compatibility. - -The size limit on messages is 1MB (while channel backends may support larger -sizes, all message formats should stay under this limit, which might include -multi-part messages where large content must be transferred). - -The length limit on channel names is 200 printable ASCII characters. - - -HTTP Request ------------- - -Represents a full-fledged, single HTTP request coming in from a client. - -Standard channel name is ``http.request``. - -Contains the following keys: - -* get: Dict of {key: [value, ...]} of GET variables (keys and values are strings) -* post: Dict of {key: [value, ...]} of POST variables (keys and values are strings) -* cookies: Dict of cookies as {cookie_name: cookie_value} (names and values are strings) -* headers: Dict of {header name: value}. Multiple headers of the same name are concatenated into one value separated by commas. -* meta: Dict of HTTP headers and info as defined in the Django Request docs (names and values are strings) -* path: String, full path to the requested page, without query string or domain -* root_path: Path designated as the "root" of the application (SCRIPT_NAME) -* method: String, upper-cased HTTP method -* server: [host, port] showing the address the client connected to -* client: [host, port] of the remote client - -Should come with an associated ``reply_channel`` which accepts HTTP Responses. - - -HTTP Response -------------- - -Sends either a part of a response or a whole response to a HTTP client - to do -streaming responses, several response messages are sent with ``more_content: True`` -and the final one has the key omitted. Normal, single-shot responses do not -need the key at all. - -Due to the 1MB size limit on messages, some larger responses will have to be -sent multi-part to stay within the limit. - -Only sent on reply channels. - -Keys that must only be in the first message of a set: - -* content_type: String, mimetype of content -* status: Integer, numerical HTTP status code -* cookies: List of cookies to set (as encoded cookie strings suitable for headers) -* headers: Dictionary of headers (key is header name, value is value, both strings) - -All messages in a set can the following keys: - -* content: String of content to send -* more_content: Boolean, signals the interface server should wait for another response chunk to stream. - - -HTTP Disconnect ---------------- - -Send when a client disconnects early, before the response has been sent. -Only sent by long-polling-capable HTTP interface servers. - -Standard channel name is ``http.disconnect``. - -Contains no keys. - - -WebSocket Connection --------------------- - -Sent when a new WebSocket is connected. - -Standard channel name is ``websocket.connect``. - -Contains the same keys as HTTP Request, without the ``POST`` or ``method`` keys. - - -WebSocket Receive ------------------ - -Sent when a datagram is received on the WebSocket. - -Standard channel name is ``websocket.receive``. - -Contains the following keys: - -* content: String content of the datagram. -* binary: Boolean, saying if the content is binary. If not present or false, content is a UTF8 string. - - -WebSocket Client Close ----------------------- - -Sent when the WebSocket is closed by either the client or the server. - -Standard channel name is ``websocket.disconnect``. - -Contains no keys. - - -WebSocket Send/Close --------------------- - -Sent by a Django consumer to send a message back over the WebSocket to -the client or close the client connection. The content is optional if close -is set, and close will happen after any content is sent, if some is present. - -Only sent on reply channels. - -Contains the keys: - -* content: String content of the datagram. -* binary: If the content is to be interpreted as text or binary. -* close: Boolean. If set to True, will close the client connection.