简体   繁体   中英

Websockets not working in my Rails app when I run on Unicorn server, but works on a Thin server

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. '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. '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) 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM