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

21
我有一个服务器,需要几分钟来处理特定的请求并回应它。客户端必须一直等待响应,而不知道什么时候会完成。有没有一种方法可以让客户端知道处理状态?(比如50%完成,80%完成),而无需客户端轮询状态。

1
相关的SO问题 - user2065736
我正在使用uwsgi。 - Thirupathi Thangavel
1
+1 uwsgi。除此之外还有什么吗?通常人们使用Bottle、Flask、Django等来处理请求。 - keredson
1
启动一个线程,并在同一时间使用您的服务器处理函数停止事件,让线程向客户端发送特定数字(每隔几秒钟或其他时间间隔),客户端应该知道这不是答案。当服务器完成工作时,设置线程停止事件(使线程停止),并将真正的响应发送给客户端。 - legend-is-back
1
很好。我添加了一个小的uwsgi服务器示例。输出与第一个服务器相同。 - D.Nibon
显示剩余3条评论
3个回答

12

在不使用任何新技术(如websockets、webpush/http2等)的情况下,我以前使用过简化的PushletLong polling解决方案来处理HTTP 1.1和各种JavaScript或自己的客户端实现。如果我的解决方案不适合您的用例,您可以随时谷歌这两个名称以获取进一步可能的方法。

客户端 发送请求,读取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.

这是一个很棒的答案。我从中学到了很多东西(而且还在不断学习中..)。谢谢你! - Thirupathi Thangavel
很高兴能够帮忙。只需记住,这个解决方案更多是因为项目限制而采用的一种变通方法,需要在所有路径上设置适当的超时等待时间等。我想我第一个使用这个解决方案的客户是在J2ME上。 :) - D.Nibon

3

这个答案可能对你的情况无法帮助,但它可能会在其他情况下有所帮助。

HTTP协议支持信息性(1xx)响应

指示中间状态响应,用于通信连接状态或请求进度,在完成请求操作并发送最终响应之前

甚至有一个特定于您的用例的状态码102(处理中)

用于通知客户端服务器已接受完整请求,但尚未完成请求操作的中间响应

状态码102由于缺乏实现而被从标准中删除,但仍然注册并可使用。

因此,它可能看起来像这样(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 标头将不可用于客户端应用程序。然而,如果服务器可以发送它们,1xx 响应仍然可能有用,因为它们的效果是重置客户端的套接字读取超时,这是慢响应的主要问题之一。

+1. 这似乎是从服务器发送处理更新的理想方式,但不幸的是我看不到在WSGI中实现它的方法。 - Thirupathi Thangavel

2
使用 分块传输编码,这是一种传输未知长度流的标准技术。
参见:维基百科 - 分块传输编码 这里提供了一个Python服务器实现,作为GitHub上的gist: 它使用标准库模块通过分块传输编码发送内容。
在客户端中,如果服务器已经通知使用分块传输编码,你只需要:
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后下一个块将是实际内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接