Worker and Background Tasks¶
While channel layers are primarily designed for communicating between different instances of ASGI applications, they can also be used to offload work to a set of worker servers listening on fixed channel names, as a simple, very-low-latency task queue.
Note
The worker/background tasks system in Channels is simple and very fast, and achieves this by not having some features you may find useful, such as retries or return values.
We recommend you use it for work that does not need guarantees around being complete (at-most-once delivery), and for work that needs more guarantees, look into a separate dedicated task queue.
Setting up background tasks works in two parts - sending the events, and then setting up the consumers to receive and process the events.
Sending¶
To send an event, just send it to a fixed channel name. For example, let’s say we want a background process that pre-caches thumbnails:
# Inside a consumer
self.channel_layer.send(
"thumbnails-generate",
{
"type": "generate",
"id": 123456789,
},
)
Note that the event you send must have a type
key, even if only one
type of message is being sent over the channel, as it will turn into an event
a consumer has to handle.
Also remember that if you are sending the event from a synchronous environment,
you have to use the asgiref.sync.async_to_sync
wrapper as specified in
channel layers.
Receiving and Consumers¶
Channels will present incoming worker tasks to you as events inside a scope
with a type
of channel
, and a channel
key matching the channel
name. We recommend you use ProtocolTypeRouter and ChannelNameRouter (see
Routing for more) to arrange your consumers:
application = ProtocolTypeRouter({
...
"channel": ChannelNameRouter({
"thumbnails-generate": consumers.GenerateConsumer.as_asgi(),
"thumbnails-delete": consumers.DeleteConsumer.as_asgi(),
}),
})
You’ll be specifying the type
values of the individual events yourself
when you send them, so decide what your names are going to be and write
consumers to match. For example, here’s a basic consumer that expects to
receive an event with type
test.print
, and a text
value containing
the text to print:
class PrintConsumer(SyncConsumer):
def test_print(self, message):
print("Test: " + message["text"])
Once you’ve hooked up the consumers, all you need to do is run a process that
will handle them. In lieu of a protocol server - as there are no connections
involved here - Channels instead provides you this with the runworker
command:
python manage.py runworker thumbnails-generate thumbnails-delete
Note that runworker
will only listen to the channels you pass it on the
command line. If you do not include a channel, or forget to run the worker,
your events will not be received and acted upon.