如何使用Django频道进行HTTP长轮询

我正在尝试为Web请求实现HTTP长轮询,但似乎无法在Channels文档中找到合适的示例,所有内容都与Web套接字有关.

使用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存储在某处,以便我以后可以回复,但我不知道如何:

>捕获组消息并生成响应
>没有收到消息时生成响应(超时),也许延迟服务器可以在这里工作?

最佳答案 所以,我最终这样做的方式如下所述.

在消费者中,如果我发现我没有立即响应发送,我将将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)
点赞