当我在 Unicorn 服务器上运行时,Websockets 在我的 Rails 应用程序中不起作用,但在瘦服务器上运行 [英] Websockets not working in my Rails app when I run on Unicorn server, but works on a Thin server

查看:15
本文介绍了当我在 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');//我在推送到 Heroku 之前更新了 URL

...在 Chrome Javascript 控制台中出现以下错误,'websocket connection to ws://0.0.0.0:3000/websocket' 失败.连接在收到握手响应之前关闭.

...当我在 Heroku 上的 Unicorn 上运行它时,我在 Chrome Javascript 控制台中遇到类似的错误,'websocket connection to ws://myapp.herokuapp.com/websocket' failed.websocket 握手期间出错.意外响应代码:500.

Heroku 日志中的堆栈跟踪显示,RuntimeError(事件机器未初始化:evma_install_oneshot_timer):

奇怪的是,当我使用命令 $rails s 在瘦服务器上本地运行它时,它运行良好.

过去五个小时我一直在网上研究这个问题,但没有找到解决方案.任何解决此问题的想法,甚至是从我的工具中获取更多信息的想法,都将不胜感激!

解决方案

更新: 我觉得很奇怪 websocket-rails 只支持基于 EventMachine 的 Web 服务器,而 <一个 href="https://github.com/faye/faye-websocket-ruby" rel="noreferrer">faye-websocket 其中 websocket-rails 基于,支持许多支持多线程的网络服务器.

经过进一步的调查和测试,我意识到我之前的假设是错误的.websocket-rails 似乎需要一个支持 rack.hijack<的多线程(因此没有 Unicorn)网络服务器,而不需要基于 EventMachine 的 Web 服务器./代码>.(Puma 符合此标准,同时是 性能可比Unicorn.)

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

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

然后……成功!

我已经能够使用 -基于EventMachine的服务器 没有 独立服务器模式.(Ruby 2.1.3p242 上的 Rails 4.1.6)

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

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

<小时>

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

  • 第一个 websocket 连接被 web 服务器接收到 (Started GET "/websocket" for ...) 但是websocket客户端的state卡在connecting,好像挂了无限期.

  • 第二次连接导致 HTTP 错误代码 500 以及 app错误:死锁;递归锁定 (ThreadError) 出现在服务器控制台输出.

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

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

有没有人知道 Unicorn 不兼容的具体技术原因?

<小时>

原始答案:

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

您使用的两个 Web 服务器的关键区别似乎是一个 (Thin) 基于 EventMachine,一个 (Unicorn>) 不是.Websocket Rails 项目维基声明 独立服务器模式必须用于非基于 EventMachine 的 Web 服务器,例如 Unicorn(这需要在 Heroku 上进行更复杂的设置,因为它需要 Redis 服务器).错误消息RuntimeError (EventMachine not initialized: evma_install_oneshot_timer):表明没有使用独立模式.

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

I'm learning Ruby on Rails to build a real-time web app with WebSockets on Heroku, but I can't figure out why the websocket connection fails when running on a Unicorn server. I have my Rails app configured to run on Unicorn both locally and on Heroku using a Procfile...

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

...which I start locally with $foreman start. The failure occurs when creating the websocket connection on the client in javascript...

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

...with the following error in the Chrome Javascript console, 'websocket connection to ws://0.0.0.0:3000/websocket' failed. Connection closed before receiving a handshake response.

...and when I run it on Unicorn on Heroku, I get a similar error in the Chrome Javascript console, 'websocket connection to ws://myapp.herokuapp.com/websocket' failed. Error during websocket handshake. Unexpected response code: 500.

The stack trace in the Heroku logs says, RuntimeError (eventmachine not initialized: evma_install_oneshot_timer):

What's strange is that it works fine when I run it locally on a Thin server using the command $rails s.

I've spent the last five hours researching this problem online and haven't found the solution. Any ideas for fixing this, or even ideas for getting more information out of my tools, would be greatly appreciated!

解决方案

UPDATE: I found it strange that websocket-rails only supported EventMachine-based web servers while faye-websocket which websocket-rails is based upon, supports many multithread-capable web servers.

After further investigation and testing, I realised that my earlier assumption had been wrong. Instead of requiring an EventMachine-based web server, websocket-rails appears to require a multithread-capable (so no Unicorn) web server which supports rack.hijack. (Puma meets this criteria while being comparable in performance to Unicorn.)

With this assumption, I tried solving the EventMachine not initialized error using the most direct method, namely, initializing EventMachine, by inserting the following code in an initializer config/initializers/eventmachine.rb:

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

and.... success!

I have been able to get Websocket Rails working on my local server over a single port using a non-EventMachine-based server without Standalone Server Mode. (Rails 4.1.6 on ruby 2.1.3p242)

This should be applicable on Heroku as long as you have no restriction in web server choice.

WARNING: This is not an officially supported configuration for websocket-rails. Care must be taken when using multithreading web servers such as Puma, as your code and that of its dependencies must be thread-safe. A (temporary?) workaround is to limit the maximum threads per worker to one and increase the number of workers, achieving a system similar to Unicorn.


Out of curiousity, I tried Unicorn again after fixing the above issue:

  • The first websocket connection was received by the web server (Started GET "/websocket" for ...) but the state of the websocket client was stuck on connecting, seeming to hang indefinitely.

  • A second connection resulted in HTTP error code 500 along with app error: deadlock; recursive locking (ThreadError) showing up in the server console output.

By the (potentially dangerous) action of removing Rack::Lock, the deadlock error can be resolved, but connections still hang, even though the server console shows that the connections were accepted.

Unsurprisingly, this fails. From the error message, I think Unicorn is incompatible due to reasons related to its network architecture (threading/concurrency). But then again, it might just be some some bug in this particular Rack middleware...

Does anyone know the specific technical reason for why Unicorn is incompatible?


ORIGINAL ANSWER:

Have you checked the ports for both the web server and the WebSocket server and their debug logs? Those error messages sound like they are connecting to something other than a WebSocket server.

A key difference in the two web servers you have used seems to be that one (Thin) is EventMachine-based and one (Unicorn) is not. The Websocket Rails project wiki states that a Standalone Server Mode must be used for non-EventMachine-based web servers such as Unicorn (which would require an even more complex setup on Heroku as it requires a Redis server). The error message RuntimeError (EventMachine not initialized: evma_install_oneshot_timer): suggests that standalone-mode was not used.

Heroku AFAIK only exposes one internal port (provided as an environmental variable) externally as port 80. A WebSocket server normally requires its own socket address (port number) (which can be worked around by reverse-proxying the WebSocket server). Websocket-Rails appears to get around this limitation by hooking into an existing EventMachine-based web server (which Unicorn does not provide) hijacking Rack.

这篇关于当我在 Unicorn 服务器上运行时,Websockets 在我的 Rails 应用程序中不起作用,但在瘦服务器上运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆