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

问题描述 投票:0回答:2

我正在尝试编写一个 API,使用 Rails 6 中的

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"}
'

我的服务器是 Puma,所以 不是因为我使用的是 Thin,如这个答案所示.

我做错了什么?如果您需要,我会提供任何可能有用的其他信息。

更新:这个问题的答案建议将

-N
Accept:text/event-stream
标头添加到请求中。这样做不会改变我在上面描述的行为——直到对
response.stream.close
的调用被触发后,才会发送对请求的响应。

更新 2:我还尝试破解

SSE#write
方法以在
broadcast()
上调用
Mutex::ConditionVariable
以强制发送消息。从它立即发送数据的意义上说,这是有效的,但是 curl 请求的副作用认为流已关闭,因此不会发送更多消息,这不是流。

更新 3:我还修改了

development.rb
以包含
config.allow_concurrency = true
,如 here 所示。上述行为没有变化。

api server-sent-events ruby-on-rails-6 puma
2个回答
8
投票

我在基本的“out the book”Rails 5 SSE 应用程序中遇到了类似的问题。问题原来是导致流缓冲的机架更新。更多信息在这里https://github.com/rack/rack/issues/1619并通过包含修复

config.middleware.delete Rack::ETag

在配置/application.rb


0
投票

此外,如果您在生产中使用 NGINX,它需要特殊配置才能使 SSE 工作。对于具有以下设置的 SSE 控制器的 URL,您应该在 nginx 配置中有特殊的

location
指令:

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

来源:EventSource / 服务器通过 Nginx 发送的事件

© www.soinside.com 2019 - 2024. All rights reserved.