Как выполнить длинный HTTP-опрос с помощью каналов Django

Я пытаюсь реализовать длинный опрос HTTP для веб-запроса, но не могу найти подходящий пример в документации по каналам, все касается веб-сокетов.

Что мне нужно сделать при использовании HTTP-сообщения:

  • дождитесь сообщения в группе, которое будет отправлено при сохранении определенной модели (возможно, с использованием сигналов)
  • дождитесь таймаута, если сообщение не получено

а затем вернуть что-нибудь клиенту.

Прямо сейчас у меня есть код, который можно увидеть в примерах:

def http_consumer(message):
    # Make standard HTTP response - access ASGI path attribute directly
    response = HttpResponse("Hello world! You asked for %s" % message.content['path'])
    # Encode that response into message format (ASGI)
    for chunk in AsgiHandler.encode_response(response):
        message.reply_channel.send(chunk)

Поэтому я должен вернуть что-то в этом http_consumer, что будет означать, что мне нечего сейчас отправлять, но я не могу заблокировать здесь. Может мне просто ничего не вернуть? Затем я должен поймать новое сообщение в определенной группе или достичь тайм-аута и отправить ответ клиенту.

Кажется, мне нужно будет где-нибудь сохранить message.reply_channel, чтобы я мог позже ответить, но я не понимаю, как:

  • поймать групповое сообщение и сгенерировать ответ
  • сгенерировать ответ, когда сообщение не было получено (тайм-аут), может быть, здесь может работать сервер задержки?

person André Cruz    schedule 25.01.2017    source источник


Ответы (1)


Итак, как я это сделал, описан ниже.

В случае потребителя, если я обнаружу, что у меня нет немедленного ответа для отправки, я сохраню message.reply_channel в группе, которая будет уведомлена в случае соответствующих событий, и запланирую отложенное сообщение, которое будет срабатывать, когда максимальное время ожидания достигается.

group_name = group_name_from_mac(mac_address)
Group(group_name).add(message.reply_channel)
message.channel_session['will_wait'] = True

delayed_message = {
    'channel': 'long_polling_terminator',
    'content': {'mac_address': mac_address,
                'reply_channel': message.reply_channel.name,
                'group_name': group_name},
    'delay': settings.LONG_POLLING_TIMEOUT
}
Channel('asgi.delay').send(delayed_message, immediately=True)

Тогда могут произойти две вещи. Либо мы получаем сообщение в соответствующей группе, и ответ отправляется раньше, либо приходит отложенное сообщение, сигнализирующее о том, что мы исчерпали время ожидания и должны вернуть ответ, указывающий, что событий не было.

Чтобы вызвать сообщение при возникновении соответствующего события, я полагаюсь на сигналы Django:

class PortalConfig(AppConfig):
    name = 'portal'

    def ready(self):
        from .models import STBMessage

        post_save.connect(notify_new_message, sender=STBMessage)

def notify_new_message(sender, **kwargs):
    mac_address = kwargs['instance'].set_top_box.id
    layer = channel_layers['default']
    group_name = group_name_from_mac(mac_address)
    response = JsonResponse({'error': False, 'new_events': True})
    group = Group(group_name)
    for chunk in AsgiHandler.encode_response(response):
        group.send(chunk)

По истечении тайм-аута я получаю сообщение на long_polling_terminator канале, и мне нужно отправить сообщение, указывающее на отсутствие событий:

def long_polling_terminator(message):
    reply_channel = Channel(message['reply_channel'])
    group_name = message['group_name']
    mac_address = message['mac_address']
    layer = channel_layers['default']
    boxes = layer.group_channels(group_name)
    if message['reply_channel'] in boxes:
        response = JsonResponse({'error': False, 'new_events': False})
        write_http_response(response, reply_channel)
        return

Последнее, что нужно сделать, - это удалить этот reply_channel из группы, и я делаю это в http.disconnect потребителе:

def process_disconnect(message, group_name_from_mac):
    if message.channel_session.get('will_wait', False):
        reply_channel = Channel(message['reply_channel'])
        mac_address = message.channel_session['mac_address']
        group_name = group_name_from_mac(mac_address)
        Group(group_name).discard(reply_channel)
person André Cruz    schedule 01.03.2017
comment
Привет @ André Cruz, мне нужно самому реализовать HTTP-опрос с использованием каналов django. Не могли бы вы упомянуть несколько статей, руководств, с которых я могу начать? Я буду очень благодарен - person krhitesh; 19.03.2019
comment
Я давно не использовал каналы Django, но мой ответ пытается объяснить именно этот сценарий. - person André Cruz; 08.05.2019