在Heroku上使用Sinatra/Thin,无法检测到HTTP流连接(SSE)客户端的断开连接。

12

我正试图在Cedar堆栈上部署一个Sinatra流式SSE响应应用程序。不幸的是,尽管在开发中它运行得非常完美,但一旦部署到Heroku上,当调用连接时callbackerrback从未被调用,导致连接池充满了过期的连接(因为服务器端仍在向它们发送数据,所以这些连接永远不会超时)。

来自Heroku文档的相关信息:

长轮询和流式响应

Cedar支持HTTP 1.1功能,如长轮询和流式响应。应用程序有30秒的初始窗口来用单个字节回复客户端。然而,之后传输的每个字节(从客户端接收或由您的应用程序发送)都会重置一个滚动的55秒窗口。如果在55秒窗口内没有发送任何数据,连接将被终止。

如果你发送了一个流式响应,比如服务器发送事件,你需要检测客户端何时挂起,并确保你的应用服务器及时关闭连接。如果服务器在55秒内保持连接处于打开状态而没有发送任何数据,你将看到一个请求超时。

这正是我想做的--检测客户端何时挂起,并及时关闭连接。但是,Heroku路由层的某些东西似乎阻止了Sinatra正常检测流关闭事件。

以下是可用于复制此问题的一些示例代码:

require 'sinatra/base'

class MyApp < Sinatra::Base

  set :path, '/tmp'
  set :environment, 'production'

  def initialize
    @connections = []

    EM::next_tick do
      EM::add_periodic_timer(1) do
        @connections.each do |out|
          out << "connections: " << @connections.count << "\n"
        end
        puts "*** connections: #{@connections.count}"
      end
    end

  end

  get '/' do
    stream(:keep_open) do |out|
      @connections << out
      puts "Stream opened from #{request.ip} (now #{@connections.size} open)"

      out.callback do
        @connections.delete(out)
        puts "Stream closed from #{request.ip} (now #{@connections.size} open)"
      end
    end
  end

end

我在 http://obscure-depths-3413.herokuapp.com/ 上放了一个示例应用程序,使用此代码说明了问题。当您连接时,连接数量将增加,但是当您断开连接时它们永远不会减少。(演示的完整源代码及Gemfile等在https://gist.github.com/mroth/5853993

我已经尽力尝试调试这个问题。有人知道如何解决吗?

P.S. 在 Sinatra 中似乎出现了类似的错误,但那是一年前修复的。此问题仅在 Heroku 的生产环境中出现,在本地运行时正常。

P.S.2. 迭代连接对象时也会出现此问题,例如添加以下代码:

EM::add_periodic_timer(10) do
  num_conns = @connections.count
  @connections.reject!(&:closed?)
  new_conns = @connections.count
  diff = num_conns - new_conns
  puts "Purged #{diff} connections!" if diff > 0
end

在本地使用良好,但在Heroku上连接似乎从未关闭。


1
当你说本地运行正常时,你确定使用了 foreman start 命令吗? - Shelvacu
使用Node.js流式传输的相同内容 https://gist.github.com/Funfun/6867167 - CodeGroover
2个回答

2
更新:在直接与Heroku路由团队(非常棒的人!)合作后,这个问题已经在其新的路由层中解决,应该可以在任何平台上正常工作。

1
我会定期手动发送“存活信号”,客户端应在接收到消息后做出响应。请查看这个简单的聊天实现https://gist.github.com/tlewin/5708745,以了解这个概念。
该应用程序使用简单的JSON协议与客户端通信。当客户端收到“alive:true”消息时,应用程序会回复响应并服务器存储最后通信时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接