我正在尝试从我的Pyramid应用程序流式传输服务器发送的事件,但是我无法从我的角度弄清楚如何流式传输响应正文。这是我正在使用的测试视图(它完全不实现SSE,仅用于计算流传输部分):
@view_config(route_name='iter_test') def iter_test(request): import time def test_iter(): i = 0 while True: i += 1 if i == 5: raise StopIteration yield str(time.time()) print time.time() time.sleep(1) return test_iter()
这产生 ValueError: Could not convert return value of the view callable function pdiff.views.iter_test into a response object. The value returned was <generator object test_iter at 0x3dc19b0>.
ValueError: Could not convert return value of the view callable function pdiff.views.iter_test into a response object. The value returned was <generator object test_iter at 0x3dc19b0>.
我尝试了一下return Response(app_iter=test_iter()),至少不会出错,但是它不会流式传输响应- 等待生成器完成后才将响应返回到我的浏览器。
return Response(app_iter=test_iter())
我认识到,这可以简单地为每个请求返回一个事件,并允许客户端在每个事件后重新连接,但是我更喜欢通过从单个请求流式传输多个事件来保持服务器发送事件的实时性,而无需重新连接延迟。我该如何使用金字塔呢?
我发现了问题。原来我的应用程序代码很好,问题出在Waitress和nginx上:
Pyramid使用的默认Web服务器Waitress以18000字节的块缓冲所有输出(有关详细信息,请参阅此问题)。
问题的根源是我放在Pyramid应用程序前面的Web服务器nginx对我隐藏的,该服务器 还 缓冲响应。
(1)可以通过以下任一方法解决:
send_bytes = 1在.ini文件中配置的女服务员。这种修复的流问题,但让你的整个应用 超 慢。正如@Zitrax所提到的,您可以使用较高的值恢复某些速度,但是任何大于1的值都可能会使消息卡在缓冲区中。
send_bytes = 1
切换到Gunicorn。我不知道gunicorn是否仅使用较小的缓冲区,或者使用时,它的性能是否更好app_iter,但是它可以正常工作,并保持我的应用程序快速运行。
app_iter
(2)可以通过配置nginx禁用流路由缓冲来解决。
您需要proxy_buffering off在nginx conf中进行设置。此设置适用于通过托管的网站proxy_pass。如果您不使用proxy_pass,则可能需要其他设置。
proxy_buffering off
proxy_pass
您可以将nginx配置为基于请求标头为每个响应动态启用/禁用缓冲,如本主题中的问题所示(EventSource / Server-Sent Events的一种很好的解决方案)
您也可以location在nginx conf 中的一个块中配置它。如果您使用的是EventSource以外的东西,并且不希望收到特定的标头,或者如果您使用的是EventSource,但想从普通的浏览器选项卡中调试响应,而无法发送Accept标头,则这样做很好根据您的要求。
location
Accept