在发送实际响应之前发送请求的间歇状态

Send intermittent status of request before sending actual response

我有一个服务器,它需要几分钟来处理一个特定的请求然后响应它。

客户端必须继续等待响应,而不知道它何时会完成。

有没有办法让客户知道处理状态? (比如完成了 50%,完成了 80%),客户端无需轮询状态。

在不使用任何更新技术(websockets、webpush/http2、...)的情况下,我以前使用过简化的 Pushlet用于 HTTP 1.1 和各种 javascript 或自己的客户端实现的长轮询 解决方案。如果我的解决方案不适合您的用例,您可以随时 google 这两个名称以获取更多可能的方法。

客户 发送请求,读取 17 个字节(初始 http 响应),然后一次读取 2 个字节以获取处理状态。

服务器 发送有效的 HTTP 响应,并在请求过程中发送 2 个字节的完成百分比,直到最后 2 个字节为 "ok" 并关闭连接。

更新:示例 uwsgi server.py

 from time import sleep
 def application(env, start_response):
     start_response('200 OK', [])

     def working():
         yield b'00'
         sleep(1)
         yield b'36'
         sleep(1)
         yield b'ok'
     return working()

更新:示例请求 client.py

import requests

response = requests.get('http://localhost:8080/', stream=True)
for r in response.iter_content(chunk_size=2):
    print(r)

示例服务器(仅用于测试:)

import socket
from time import sleep
HOST, PORT = '', 8888

listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_socket.bind((HOST, PORT))
listen_socket.listen(1)

while True:
    client_connection, client_address = listen_socket.accept()
    request = client_connection.recv(1024)
    client_connection.send('HTTP/1.1 200 OK\n\n')
    client_connection.send('00')  # 0%
    sleep(2)  # Your work is done here
    client_connection.send('36')  # 36%
    sleep(2)  # Your work is done here
    client_connection.sendall('ok')  # done
    client_connection.close()

如果最后 2 个字节不是 "ok",请以其他方式处理错误。这不是漂亮的 HTTP 状态代码合规性,而是多年前对我有用的解决方法。

telnet 客户端示例

$ telnet localhost 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
HTTP/1.1 200 OK

0036okConnection closed by foreign host.

使用分块传输编码,这是传输未知长度流的标准技术。

参见:Wikipedia - Chunked Transfer Encoding

这里有一个 python 服务器实现,可作为 GitHub 上的要点:

它使用标准库模块使用分块传输编码发送内容

在客户端,如果服务器通知了分块传输编码,你只需要:

import requests

response = requests.get('http://server.fqdn:port/', stream=True)
for r in response.iter_content(chunk_size=None):
    print(r)

chunk_size=None,因为块是动态的,将由块传输语义的简单约定中的信息确定。

参见:http://docs.python-requests.org/en/master/user/advanced/#chunk-encoded-requests

当您在响应 r 的内容中看到例如 100 时,您知道下一个块将是处理 100 后的实际内容。

此答案可能对您的特定情况没有帮助,但在其他情况下可能会有所帮助。

HTTP协议支持informational (1xx) responses:

indicates an interim response for communicating connection status or request progress prior to completing the requested action and sending a final response

甚至还有一个状态代码正好适合您的用例,102 (Processing):

interim response used to inform the client that the server has accepted the complete request, but has not yet completed it

状态代码 102 由于缺少实现而从该标准的后续版本中删除,但它仍然是 registered 并且可以使用。

因此,它可能看起来像这样(HTTP/2 具有等效的二进制形式):

HTTP/1.1 102 Processing
Progress: 50%

HTTP/1.1 102 Processing
Progress: 80%

HTTP/1.1 200 OK
Date: Sat, 05 Aug 2017 11:53:14 GMT
Content-Type: text/plain

All done!

不幸的是,这没有得到广泛支持。特别是,WSGI 不提供发送任意 1xx 响应的方法。客户端支持 1xx 响应,因为它们 需要解析和容忍它们 ,但它们通常不提供对它们的编程访问:在本例中,Progress header 对客户端应用程序不可用。

但是,1xx 响应可能仍然有用(如果服务器可以发送它们),因为它们具有 重置客户端套接字读取超时的效果,这是主要的功能之一响应慢的问题。