mirror of
https://github.com/django/daphne.git
synced 2025-04-21 17:22:03 +03:00
Stats extension, application abstraction notes for ASGI
This commit is contained in:
parent
6a5907ff59
commit
e78f75288d
|
@ -90,6 +90,12 @@ to have a process that does both, or middleware-like code that transforms
|
|||
messages between two different channel layers or channel names. It is
|
||||
expected, however, that most deployments will fall into this pattern.
|
||||
|
||||
There is even room for a WSGI-like application abstraction with a callable
|
||||
which takes ``(channel, message, send_func)``, but this would be slightly
|
||||
too restrictive for many use cases and does not cover how to specify
|
||||
channel names to listen on; it is expected that frameworks will cover this
|
||||
use case.
|
||||
|
||||
|
||||
Channels and Messages
|
||||
---------------------
|
||||
|
@ -180,8 +186,9 @@ not required for basic application code and nearly all protocol server
|
|||
code, and so has been made optional in order to encourage lighter-weight
|
||||
channel layers to be written.
|
||||
|
||||
The only extension in this document is the ``groups`` extension, defined
|
||||
below.
|
||||
There are two extensions defined here: the ``groups`` extension, which
|
||||
is expanded on below, and the ``statistics`` extension, which allows
|
||||
channel layers to provide global and per-channel statistics.
|
||||
|
||||
There is potential to add further extensions; these may be defined by
|
||||
a separate specification, or a new version of this specification.
|
||||
|
@ -212,25 +219,15 @@ the group feature is still optional; its presence is indicated by the
|
|||
Thus, there is a simple Group concept in ASGI, which acts as the
|
||||
broadcast/multicast mechanism across channels. Channels are added to a group,
|
||||
and then messages sent to that group are sent to all members of the group.
|
||||
Channels expire from being in a group after a certain amount of time,
|
||||
and must be refreshed periodically to remain in it, and can also be
|
||||
explicitly removed.
|
||||
Channels can be removed from a group manually (e.g. based on a disconnect
|
||||
event), and the channel layer will garbage collect "old" channels in groups
|
||||
on a periodic basis.
|
||||
|
||||
The expiry is because this specification assumes that at some point
|
||||
message delivery will fail, and so disconnection events by themselves
|
||||
are not sufficient to tie to an explicit group removal - over time, the
|
||||
number of group members will slowly increase as old response channels
|
||||
leak as disconnections get dropped.
|
||||
|
||||
Instead, all protocol servers that have an ongoing connection
|
||||
(for example, long-poll HTTP or WebSockets) will instead send periodic
|
||||
"keepalive" messages, which can be used to refresh the response channel's
|
||||
group membership - each call to ``group_add`` should reset the expiry timer.
|
||||
|
||||
Keepalive message intervals should be one-third as long as the group expiry
|
||||
timeout, to allow for slow or missed delivery of keepalives; protocol servers
|
||||
and anything else sending keepalives can retrieve the group expiry time from
|
||||
the channel layer in order to do this correctly.
|
||||
How this garbage collection happens is not specified here, as it depends on
|
||||
the internal implementation of the channel layer. The recommended approach,
|
||||
however, is when a message on a single-listener channel expires, the channel
|
||||
layer should remove that channel from all groups it's currently a member of;
|
||||
this is deemed an acceptable indication that the channel's listener is gone.
|
||||
|
||||
*Implementation of the group functionality is optional*. If it is not provided
|
||||
and an application or protocol server requires it, they should hard error
|
||||
|
@ -301,7 +298,8 @@ A *channel layer* should provide an object with these attributes
|
|||
A channel layer implementing the ``groups`` extension must also provide:
|
||||
|
||||
* ``group_add(group, channel)``, a callable that takes a ``channel`` and adds
|
||||
it to the group given by ``group``. Both are byte strings.
|
||||
it to the group given by ``group``. Both are byte strings. If the channel
|
||||
is already in the group, the function should return normally.
|
||||
|
||||
* ``group_discard(group, channel)``, a callable that removes the ``channel``
|
||||
from the ``group`` if it is in it, and does nothing otherwise.
|
||||
|
@ -310,8 +308,22 @@ A channel layer implementing the ``groups`` extension must also provide:
|
|||
arguments; the group to send to, as a byte string, and the message
|
||||
to send, as a serializable ``dict``.
|
||||
|
||||
* ``group_expiry``, an integer number of seconds describing the minimum
|
||||
group membership age before a channel is removed from a group.
|
||||
A channel layer implementing the ``statistics`` extension must also provide:
|
||||
|
||||
* ``global_statistics()``, a callable that returns a dict with zero
|
||||
or more of (unicode string keys):
|
||||
|
||||
* ``count``, the current number of messages waiting in all channels
|
||||
|
||||
* ``channel_statistics(channel)``, a callable that returns a dict with zero
|
||||
or more of (unicode string keys):
|
||||
|
||||
* ``length``, the current number of messages waiting on the channel
|
||||
* ``age``, how long the oldest message has been waiting, in seconds
|
||||
* ``per_second``, the number of messages processed in the last second
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Channel Semantics
|
||||
|
@ -338,6 +350,24 @@ have a "resilience testing" mode where they deliberately drop more messages
|
|||
than usual so developers can test their code's handling of these scenarios.
|
||||
|
||||
|
||||
Persistence
|
||||
-----------
|
||||
|
||||
Channel layers do not need to persist data long-term; group
|
||||
memberships only need to live as long as a connection does, and messages
|
||||
only as long as the message expiry time, which is usually a couple of minutes.
|
||||
|
||||
That said, if a channel server goes down momentarily and loses all data,
|
||||
persistent socket connections will continue to transfer incoming data and
|
||||
send out new generated data, but will have lost all of their group memberships
|
||||
and in-flight messages.
|
||||
|
||||
In order to avoid a nasty set of bugs caused by these half-deleted sockets,
|
||||
protocol servers should quit and hard restart if they detect that the channel
|
||||
layer has gone down or lost data; shedding all existing connections and letting
|
||||
clients reconnect will immediately resolve the problem.
|
||||
|
||||
|
||||
Message Formats
|
||||
---------------
|
||||
|
||||
|
@ -376,7 +406,7 @@ them; if a protocol server or connection incapable of Server Push receives
|
|||
these, it should simply drop them.
|
||||
|
||||
The HTTP specs are somewhat vague on the subject of multiple headers;
|
||||
RFC7230 explicitly says they must be mergeable with commas, while RFC6265
|
||||
RFC7230 explicitly says they must be merge-able with commas, while RFC6265
|
||||
says that ``Set-Cookie`` headers cannot be combined this way. This is why
|
||||
request ``headers`` is a ``dict``, and response ``headers`` is a list of
|
||||
tuples, which matches WSGI.
|
||||
|
@ -758,6 +788,12 @@ TODOs
|
|||
behind it as it's nonblocking - possible alternative call type?
|
||||
Asyncio extension that provides ``receive_many_yield``?
|
||||
|
||||
* Possible extension to allow detection of channel layer flush/restart and
|
||||
prompt protocol servers to restart?
|
||||
|
||||
* Maybe WSGI-app like spec for simple "applications" that allows standardized
|
||||
application-running servers?
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
|
Loading…
Reference in New Issue
Block a user