Rails + Puma + 传输编码:分块响应 - 支持与否?
Rails + Puma + Transfer-Encoding: chunked response - supported or not?
配置
- Rails: 4.2.7.1
- 美洲狮:3.8.2
--
Transfer-Encoding: chunked
我一直无法完成这项工作,也找不到明确的答案:在上面的配置中,我想将大量数据流式传输到客户端(在响应中)- 这是否受支持?
- 如果是这样,我的责任是什么?
- 我的控制器是否应该发出十六进制块大小,
\r\n
、0
等?
- 感觉我想要但找不到像这样的简单回复API:
write
、write
、write
、flush
write
、write
、write
、flush
close
- 我已经阅读了 100 篇关于 Rack、monkey patching 和其他疯狂的帖子
- 我读过 Puma and/or 机架破坏编码,可能 gzip/deflating 顺序错误
- 这似乎是一个应该随时可用的简单功能,但我很困惑
- 我创建了许多测试,例如
self.response_body = Enumerator.new
和 response.stream.write
等——都具有相似的结果(通过 curl
)——Malformed encoding found in chunked-encoding
或 transfer closed with outstanding read data remaining
任何人都可以告诉我灯吗?
ActionController::Live API 完全符合您的描述:response.stream.write
和 response.stream.close
。 (write
自动刷新一个块;如果这对你不起作用,你需要自己做缓冲。)
只要你include ActionController::Live
(注意这会影响整个控制器的行为,而不仅仅是一个动作),你应该能够毫不费力地编写流式响应:你不需要,并且不应设置与分块相关的任何 headers 等。
@matthewd 精彩回答的重要补充:
Rack 规范支持通过 the use of the each
method on the response object or through the use of hijack
的流式响应。
@matthewd 说得对:
The ActionController::Live API offers exactly what you describe...
但是,实现要么劫持套接字,要么使用 Rack 规范的 "hack" 和 each
方法。
最好的情况是实现劫持套接字并在新线程上运行它(这是它通常应该做的,AFAIK)。
但是,这可能会导致大量线程,并且 可能会导致性能变差 - 线程在内存中的成本 space 用于堆栈数据(每个线程 1Mb-2Mb / 客户端)和上下文切换变得更加昂贵,因为更多的线程被创建。
在最坏的情况下,缓慢的 each
循环会阻塞服务器的线程,使服务器瘫痪并最终导致 DoS 情况。
正确的答案应该不是通过单个 HTTP 请求流式传输数据 - 请改用 native* WebSockets、SSE 或 AJAX 解决方案。
另一种半正确的方法是将所有数据保存到一个临时文件中,并使用支持静态文件流的服务器发送文件到 Ruby 层之外(例如 iodine)或代理(例如nginx)。
* 本机:本机 WebSocket / SSE 解决方案遵循 this Rack proposal and allows the server to handle the network layer rather than running another thread / IO reactor. See this blog post 以获取更多详细信息。
配置
- Rails: 4.2.7.1
- 美洲狮:3.8.2
--
Transfer-Encoding: chunked
我一直无法完成这项工作,也找不到明确的答案:在上面的配置中,我想将大量数据流式传输到客户端(在响应中)- 这是否受支持?
- 如果是这样,我的责任是什么?
- 我的控制器是否应该发出十六进制块大小,
\r\n
、0
等?
- 我的控制器是否应该发出十六进制块大小,
- 感觉我想要但找不到像这样的简单回复API:
write
、write
、write
、flush
write
、write
、write
、flush
close
- 我已经阅读了 100 篇关于 Rack、monkey patching 和其他疯狂的帖子
- 我读过 Puma and/or 机架破坏编码,可能 gzip/deflating 顺序错误
- 这似乎是一个应该随时可用的简单功能,但我很困惑
- 我创建了许多测试,例如
self.response_body = Enumerator.new
和response.stream.write
等——都具有相似的结果(通过curl
)——Malformed encoding found in chunked-encoding
或transfer closed with outstanding read data remaining
任何人都可以告诉我灯吗?
ActionController::Live API 完全符合您的描述:response.stream.write
和 response.stream.close
。 (write
自动刷新一个块;如果这对你不起作用,你需要自己做缓冲。)
只要你include ActionController::Live
(注意这会影响整个控制器的行为,而不仅仅是一个动作),你应该能够毫不费力地编写流式响应:你不需要,并且不应设置与分块相关的任何 headers 等。
@matthewd 精彩回答的重要补充:
Rack 规范支持通过 the use of the each
method on the response object or through the use of hijack
的流式响应。
@matthewd 说得对:
The ActionController::Live API offers exactly what you describe...
但是,实现要么劫持套接字,要么使用 Rack 规范的 "hack" 和 each
方法。
最好的情况是实现劫持套接字并在新线程上运行它(这是它通常应该做的,AFAIK)。
但是,这可能会导致大量线程,并且 可能会导致性能变差 - 线程在内存中的成本 space 用于堆栈数据(每个线程 1Mb-2Mb / 客户端)和上下文切换变得更加昂贵,因为更多的线程被创建。
在最坏的情况下,缓慢的 each
循环会阻塞服务器的线程,使服务器瘫痪并最终导致 DoS 情况。
正确的答案应该不是通过单个 HTTP 请求流式传输数据 - 请改用 native* WebSockets、SSE 或 AJAX 解决方案。
另一种半正确的方法是将所有数据保存到一个临时文件中,并使用支持静态文件流的服务器发送文件到 Ruby 层之外(例如 iodine)或代理(例如nginx)。
* 本机:本机 WebSocket / SSE 解决方案遵循 this Rack proposal and allows the server to handle the network layer rather than running another thread / IO reactor. See this blog post 以获取更多详细信息。