Heroku 上的 Sinatra/Thin 未检测到 HTTP 流连接 (SSE) 客户端断开连接

Posted

技术标签:

【中文标题】Heroku 上的 Sinatra/Thin 未检测到 HTTP 流连接 (SSE) 客户端断开连接【英文标题】:HTTP streaming connection (SSE) client disconnect not detected with Sinatra/Thin on Heroku 【发布时间】:2013-06-21 13:53:29 【问题描述】:

我正在尝试在 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)

我在尝试调试这个时不知所措。有人知道怎么解决吗?

附:似乎有一个similar bug in Sinatra,但它是在一年前修复的。此外,此问题仅发生在 Heroku 的生产环境中,但在本地运行时可以正常工作。

附注 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 上从未显示为已关闭。

【问题讨论】:

当你说它在本地运行时运行良好,你确定使用foreman start吗? 使用 Node.js 流式处理同样的事情gist.github.com/Funfun/6867167 【参考方案1】:

更新:在直接与 Heroku 路由团队(他们是很棒的人!)合作后,这个问题现在在他们的新路由层中得到修复,并且应该在任何平台上都能正常工作。

【讨论】:

【参考方案2】:

我会定期手动发送活动信号,如果收到消息,客户端应该响应该信号。

请看一下这个简单的聊天实现https://gist.github.com/tlewin/5708745,它说明了这个概念。

应用程序使用简单的 JSON 协议与客户端通信。当客户端收到alive: true 消息时,应用程序会回传一个响应,服务器会存储最后一次通信时间。

【讨论】:

以上是关于Heroku 上的 Sinatra/Thin 未检测到 HTTP 流连接 (SSE) 客户端断开连接的主要内容,如果未能解决你的问题,请参考以下文章

如何让我在 Heroku 上的 Laravel 项目使用 Heroku Postgres?

加快 Heroku 上的部署 [重复]

Heroku 上的 PSQLException

Heroku 上的 CodeIgniter 返回 404

Heroku 上的 Phoenix:错误 R10

Heroku 上的 Symfony:403 Forbidden 您无权访问此服务器上的 /