我正在学习 Ruby on Rails 以在 Heroku 上使用 WebSockets 构建实时网络应用程序,但我无法弄清楚为什么在 Unicorn 服务器上运行时 websocket 连接失败。我将我的 Rails 应用程序配置为在本地和 Heroku 上使用 Procfile 在 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' 失败。在收到握手响应之前连接已关闭。
...当我在 Heroku 的 Unicorn 上运行它时,我在 Chrome Javascript 控制台中遇到了类似的错误,'websocket connection to ws://myapp.herokuapp.com/websocket' 失败。 websocket 握手期间出错。意外的响应代码:500。
Heroku 日志中的堆栈跟踪显示,RuntimeError (eventmachine not initialized: evma_install_oneshot_timer):
奇怪的是,当我使用命令 $rails s
在 Thin 服务器上本地运行它时,它工作正常。
我花了最后五个小时在线研究这个问题,但没有找到解决方案。任何解决此问题的想法,甚至是从我的工具中获取更多信息的想法,都将不胜感激!
最佳答案
更新: 我发现很奇怪 websocket-rails 只支持基于 EventMachine 的网络服务器,而 faye-websocket websocket-rails 基于它,支持 many multithread-capable web servers .
经过进一步的调查和测试,我意识到我之前的假设是错误的。 websocket-rails 似乎不需要基于 EventMachine 的 Web 服务器,而是需要支持 rack.hijack< 的多线程(因此没有 Unicorn)Web 服务器
。 (Puma 符合此标准,同时是 Unicorn 的 comparable in performance。)
基于这个假设,我尝试使用最直接的方法解决 EventMachine not initialized
错误,即通过在初始化程序 config/initializers/eventmachine 中插入以下代码来初始化 EventMachine。 rb
:
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
然后....成功!
我已经能够使用 non-EventMachine-based server 通过单个端口在我的本地服务器上运行 Websocket Rails 没有 Standalone Server Mode . ( ruby 2.1.3p242 上的 Rails 4.1.6)
只要您对 Web 服务器的选择没有限制,这应该适用于 Heroku。
警告:这不是 websocket-rails 官方支持的配置。使用 Puma 等多线程 Web 服务器时必须小心,因为您的代码及其依赖项的代码必须是线程安全的。 A(临时的?)workaround就是将每个worker的最大线程限制为一个,并增加worker的数量,实现类似Unicorn的系统。
出于好奇,我在解决上述问题后再次尝试了Unicorn:
Web 服务器接收到第一个 websocket 连接(
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 项目维基声明 Standalone Server Mode必须用于非基于 EventMachine 的 Web 服务器,例如 Unicorn(这需要在 Heroku 上进行更复杂的设置,因为它需要 Redis 服务器)。 错误消息 。RuntimeError (EventMachine not initialized: evma_install_oneshot_timer):
表明未使用独立模式
Heroku AFAIK 仅公开 one internal port (作为环境变量提供)在外部作为端口 80。WebSocket 服务器通常需要自己的套接字地址(端口号)(可以通过反向代理 WebSocket 服务器来解决)。 Websocket-Rails 似乎通过挂接到现有的基于 EventMachine 的 Web 服务器(Unicorn 不提供) 来绕过此限制 hijacking Rack .
关于javascript - 当我在 Unicorn 服务器上运行时,Websockets 在我的 Rails 应用程序中不起作用,但在瘦服务器上运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25657405/