简体   繁体   中英

Django channels page stuck on loading

I created a simple Django Channels consumer that connects to a Redis channel and receives some data from this channel, i want to send this data to the frontend.

The consumer is able to connect to the consumer and receives the data; the problem is that if i try to load the page when the consumer is running, the page will be stuck on loading. I'm sure this happens because the connection to the Redis channel is a blocking operation, or it could be a problem with threads. I'm new to this concepts, so i decided to make a question for it.

Here is my consumer:

class EchoConsumer(AsyncConsumer):

    async def websocket_connect(self, event):
        self.send({
                'type': 'websocket.accept'
            })

        self.receive(event)

    def receive(self, event):
        redis_url = 'redis://localhost:6379/0'
        connection = redis.StrictRedis.from_url(redis_url, decode_responses=True)
        channel = 'TEST'
        params = urllib.parse.parse_qs(self.scope.get('query_string', b'').decode('utf-8'))

        pubsub = connection.pubsub(ignore_subscribe_messages=True)
        pubsub.subscribe(channel)

        for message in pubsub.listen():

            # self.send({
            #   'type': 'websocket.send',
            #   'text': message['data'],
            # })

            print(message['data'])


    async def websocket_disconnect(self, event):
        print('DISCONNECTED!')

So what happens is that i can see the data being printed to my console, but if i try to leave that page and reach a different part of my site, the page will get stuck on loading. Can anyone help me fix this?

There are 2 things you could be trying to do here.

Subscribing once and sending this information to all open connections

the issue you are having is pubsub.listen(): will loop forever (never stopping). So your consumer will never continue and be able to process any more messages.

Since (at least in this example) it looks like you are always hitting redis with static values (not dependent on the request from the user) you are better off doing this subscription outside of your consumer. (in a django command https://docs.djangoproject.com/en/3.0/howto/custom-management-commands/ )

Then you can have that command then send these messages over a channel layer to your subscribed consumers.

this would make your consumer look like this

class EchoConsumer(AsyncJsonWebsocketConsumer):
    async def on_message(self, message):
        await self.send_json(message)

then in your management command instread of printing you can send the message using


async_to_sync(channel_layer.group_send)(
        "echo_group",
        {"type": "on.message", "rate":Rate, "quantity": Quantity, "symbol": Symbol, "order": Order},
    )

Subscribing once for each open connection

This you should only do if you expect the subscription to be different for each websocket connection. (eg you are using a value in the url/query/headers or subscribing only when the user sends a ws message to your consumer with a given filter value).

Doing this is a LOT more complex for a few reasons:

  1. Redis will not handle as many open connections to it as you can have websocket connections.

  2. You need to setup a nested async task that can handle the events from redis in such a way that they do not block the rest of the consumer.

If you do still need this functionality i'm happy to update the answer with a solution (but warning it will be long).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM