gevent.http.HTTPServer API建议流式处理,但却缓存整个请求和响应
gevent.http.HTTPServer
提供的接口看起来支持双向流式传输。请求对象并不是简单地把请求体作为一个字符串提供,而是提供了一个 .input_buffer
属性,这个属性是一个可以逐个读取的 Python 可迭代对象。而在响应的方向上,数据可以通过三次调用分块发送:
request.send_reply_start(200, 'OK')
request.send_reply_chunk(...) # as many times as you wish
request.send_reply_end()
不过我可能配置错了,因为尽管这个 API 没有缓冲,我的请求处理程序直到最后一块 POST 数据到达时才会被调用。而在响应方面,我的客户端套接字在服务器调用 .send_reply_end()
之前根本看不到任何头信息。我是不是需要切换某个开关,或者调整某个配置,才能关闭缓冲,像 gevent 通过 StreamServer
支持原始套接字那样,实时处理请求和发送响应呢?
我的应用需要支持单文件上传和下载,这些文件可能会大于内存,这就需要关闭缓冲。
下面是一个用 gevent 编写的简单服务器和客户端,应该能展示这种行为:
# srv.py
import gevent.http
M100 = 100 * 1024 * 1024
def main():
print 'Serving on 8088...'
gevent.http.HTTPServer(('0.0.0.0', 8088), handle).serve_forever()
def handle(request):
print 'Is request chunked?', request.chunked
for item in request.input_buffer:
print 'received body segment of length', len(item), 'bytes'
request.add_output_header('Content-Type', 'application/octet-stream')
request.send_reply_start(200, 'OK')
for i in range(5):
print 'sending chunk', i
request.send_reply_chunk(M100 * 'x')
request.send_reply_end()
if __name__ == '__main__':
main()
还有:
# cli.py
import requests
import time
M100 = 100 * 1024 * 1024
def gen():
for i in range(5):
print 'sending chunk', i
yield M100 * 'x'
time.sleep(1)
if __name__ == '__main__':
r = requests.post('http://localhost:8088/', data=gen(), stream=True)
for block in r.iter_content(M100):
print 'received', len(block), 'bytes from download'
谢谢你的指导!
1 个回答
0
请求服务器:从源代码来看,服务器的处理函数在请求完成之前是不会被调用的,无论请求是以分块的方式发送还是其他方式。所以在这方面你可能会遇到问题。
服务器响应:服务器的响应可以以流式的方式发送,但你需要在处理线程中主动让出控制权,这样才能实现(比如在每发送一块数据后调用 gevent.sleep()
)。
不幸的是,gevent
似乎没有提供一种方法来等待某一块数据发送完成后再开始发送下一块,所以如果你生成数据的速度比发送的速度快,可能会出现内存问题。
请注意,上述信息是针对 gevent<1.0
的,它使用了 libevent
库,而不适用于更新版本的 gevent
。当前版本的 gevent
不再包含 http
模块,所以这个问题就不再适用了。