当我在Unicorn服务器上运行时,Websockets在我的Rails应用程序中不起作用,但在瘦服务器上运行

我正在学习Ruby on Rails,在Heroku上使用WebSockets构建一个实时Web应用程序,但我无法弄清楚为何在Unicorn服务器上运行时websocket连接失败。 我将我的Rails应用程序配置为使用Procfile在本地和Heroku上运行Unicorn …

web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb 

…我在本地开始与$foreman start 。 在javascript中在客户端上创建websocket连接时发生故障…

 var dispatcher = new WebSocketRails('0.0.0.0:3000/websocket'); //I update the URL before pushing to Heroku 

…在Chrome Javascript控制台中出现以下错误, 'websocket connection to ws://0.0.0.0:3000/websocket' failed. Connection closed before receiving a handshake response. 'websocket connection to ws://0.0.0.0:3000/websocket' failed. Connection closed before receiving a handshake response.

…当我在Heroku上的Unicorn上运行它时,我在Chrome Javascript控制台中遇到了类似的错误, 'websocket connection to ws://myapp.herokuapp.com/websocket' failed. Error during websocket handshake. Unexpected response code: 500. 'websocket connection to ws://myapp.herokuapp.com/websocket' failed. Error during websocket handshake. Unexpected response code: 500.

Heroku日志中的堆栈跟踪说, RuntimeError (eventmachine not initialized: evma_install_oneshot_timer):

奇怪的是,当我使用命令$rails s在Thin服务器上本地运行它时它工作正常。

我花了最后五个小时在线研究这个问题并且没有找到解决方案。 任何解决这个问题的想法,甚至是从我的工具中获取更多信息的想法,都将不胜感激!

更新:我发现很奇怪websocket-rails只支持基于EventMachine的Web服务器,而websocket-rails所基于的faye-websocket则支持许多支持multithreading的Web服务器 。

经过进一步的调查和测试,我意识到我之前的假设是错误的。 websocket-rails似乎不需要基于EventMachine的Web服务器,而是需要支持multithreading(因此不支持Unicorn )的web服务器,它支持rack.hijack 。 ( Puma符合此标准,但在性能上与Unicorn 相当 。)

有了这个假设,我尝试使用最直接的方法解决EventMachine not initialized错误,即初始化EventMachine,方法是在初始化程序config/initializers/eventmachine.rb插入以下代码:

 Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive? 

…… 成功!

我已经能够使用没有 独立服务器模式基于事件的服务器通过单个端口在我的本地服务器上运行Websocket Rails 。 (关于ruby 2.1.3p242的Rails 4.1.6)

只要您对Web服务器选择没有限制,这应该适用于Heroku。

警告:这不是websocket-rails的官方支持配置。 使用multithreadingWeb服务器(如Puma)时必须小心,因为您的代码及其依赖项的代码必须是线程安全的。 (临时?) 解决方法是将每个工作者的最大线程数限制为1并增加工作者数量,从而实现类似于Unicorn的系统。


出于好奇,我在解决上述问题后再次尝试了Unicorn

  • 第一个websocket连接由Web服务器接收( Started GET "/websocket" for ... )但是websocket客户端的state卡在connecting ,似乎无限期挂起。

  • 第二个连接导致HTTP错误代码500以及app error: deadlock; recursive locking (ThreadError) 在服务器控制台输出中显示的app error: deadlock; recursive locking (ThreadError)

通过删除Rack::Lock的(可能危险的)操作,可以解决死锁错误,但是连接仍然挂起,即使服务器控制台显示连接已被接受。

不出所料,这失败了。 从错误消息中,我认为Unicorn由于与其网络架构(线程/并发)相关的原因而不兼容。 但话说回来,它可能只是这个特定Rack中间件中的一些错误……

有谁知道为什么Unicorn不兼容的具体技术原因?


原始答案:

您是否检查过Web服务器和WebSocket服务器的端口及其调试日志? 这些错误消息听起来像是连接到WebSocket服务器以外的其他东西。

您使用的两个Web服务器的主要区别似乎是一个( Thin )是基于EventMachine而一个( Unicorn )不是。 Websocket Rails项目维基表示, 独立服务器模式必须用于非基于EventMachine的Web服务器,例如Unicorn(由于需要Redis服务器,因此需要在Heroku上进行更复杂的设置)。 错误消息RuntimeError (EventMachine not initialized: evma_install_oneshot_timer):表明未使用独立模式

Heroku AFAIK只在外部公开一个内部端口 (作为环境变量提供)作为端口80.WebSocket服务器通常需要自己的套接字地址(端口号)(可以通过反向代理WebSocket服务器来解决)。 Websocket-Rails似乎通过挂钩现有的基于EventMachine的Web服务器(Unicorn不提供) 劫持Rack来解决这个限制。