服务器在Rails中发送的事件不是异步传递的



我正在尝试编写一个API,该API使用Rails6中的ActionController::Live::SSE提供服务器敏感事件。为了理解如何最好地编写测试,我从基本上复制这里看到的琐碎示例开始:my_controller.rb:

class MyController < ApplicationController
include ActionController::Live
def capture
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream)
3.times do
sse.write({message: "Awaiting confirmation ..."})
sleep 2
end
fake_response  = { #The response as hash.
"annotation_id"=>nil,
"domain"=>"some.random.com",
"id"=>2216354,
"path"=>"/flummoxer/",
"protocol"=>"https",
}
sse.write(fake_response, event: 'successful capture')
rescue => e
sse.write(e.message, event: 'something broke: ')
ensure
response.stream.close
end
end

当我向这个端点发送curl请求(无论是POST还是GET(时,响应都会在一个块中到达,而不是作为单独的响应:

$ curl -i -X GET -H  "Content-Type: application/json" -d '{"url": "https://some.random.com/flummoxer"}' http://localhost:3000/capture
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
ETag: W/"a24048695d2feca40232467f0fbb410a"
X-Request-Id: 648a5229-a43d-40d3-82fd-1c4ea6fe19cc
X-Runtime: 24.082528
Transfer-Encoding: chunked
data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
event: successful capture
data: {"annotation_id":null,"domain":"some.random.com","id":2216354,"path":"/flummoxer/","protocol":"https"}

在我的测试中,试图解析来自服务器的响应失败,这一点可以更容易地看出:

MultiJson::ParseError: 783: unexpected token at 'data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
data: {"message":"Awaiting confirmation ..."}
event: successful capture
data: {"annotation_id":null,"domain":"some.random.com","id":2216354,"path":"/flummoxer/","protocol":"https"}
'

我的服务器是彪马,所以这并不是因为我在使用瘦,正如这个答案所示。

我做错了什么?如果你问的话,我会提供任何可能有用的额外信息。

更新:这个问题的答案建议将-NAccept:text/event-stream头都添加到请求中。这样做并不会改变我上面描述的行为——直到触发对response.stream.close的调用,才会发送对请求的响应。

更新2:我还尝试过破解SSE#write方法,在Mutex::ConditionVariable上调用broadcast()来强制发送消息。这是有效的,因为它会立即发送数据,但有curl请求认为流已关闭的副作用,因此不会发送更多消息,这不是流。

更新3:我还修改了development.rb以包含config.allow_concurrency = true,如下所示。上述行为没有变化。

我在一个基本的"书外"Rails 5 SSE应用程序中遇到了类似的问题。问题是机架更新导致流缓冲。更多信息请点击此处https://github.com/rack/rack/issues/1619并通过包括进行修复

config.midleware.delete Rack::ETag

在config/application.rb 中

此外,如果您在生产中使用NGINX,则需要对SSE进行特殊配置才能工作。您应该在nginx-config中为带有SSE控制器的URL提供特殊的location指令,设置如下:

proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;

来源:EventSource/Server通过Nginx 发送事件

最新更新