Веб-сокеты не работают в моем приложении Rails при работе на сервере Unicorn, но работают на тонком сервере

Я изучаю Ruby on Rails для создания веб-приложения в реальном времени с WebSockets на Heroku, но я не могу понять, почему происходит сбой соединения websocket при работе на сервере Unicorn. У меня есть приложение Rails, настроенное для работы на Unicorn как локально, так и на Heroku с использованием Procfile...

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

... с которого я начинаю локально $foreman start, Ошибка возникает при создании подключения веб-сокета на клиенте в JavaScript...

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.

... и когда я запускаю его на Unicorn на Heroku, я получаю похожую ошибку в консоли Chrome Javascript, '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,

Я провел последние пять часов, исследуя эту проблему онлайн, и не нашел решения. Буду очень признателен за любые идеи по исправлению этого или даже за идеи получить больше информации из моих инструментов!

1 ответ

Решение

ОБНОВЛЕНИЕ: Мне показалось странным, что рельсы websocket поддерживают только веб-серверы на основе EventMachine, в то время как faye-websocket, на котором основан websocket-rails, поддерживает множество многопоточных веб-серверов.

После дальнейших исследований и испытаний я понял, что мое предыдущее предположение было неверным. Вместо того, чтобы требовать веб-сервер на основе EventMachine, для websocket-rails требуется многопоточный веб-сервер с поддержкой многопоточности, который поддерживает 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 работать на моем локальном сервере через один порт, используя сервер, не основанный на EventMachine, без режима автономного сервера. (Rails 4.1.6 на ruby ​​2.1.3p242)

Это должно быть применимо к Heroku, если у вас нет ограничений в выборе веб-сервера.

ВНИМАНИЕ: Это официально не поддерживаемая конфигурация для рельсов websocket. Необходимо соблюдать осторожность при использовании многопоточных веб-серверов, таких как Puma, поскольку ваш код и его зависимости должны быть поточно-ориентированными. (Временный?) Обходной путь - ограничить максимальное количество потоков на одного работника и увеличить количество работников, создав систему, аналогичную Unicorn.


Из любопытства я попробовал Unicorn снова после исправления вышеуказанной проблемы:

  • Первое соединение с веб-сокетом было получено веб-сервером (Started GET "/websocket" for ...) но state клиента websocket застрял на connecting Казалось бы, зависать до бесконечности.

  • Второе соединение привело к HTTP-коду ошибки 500 вместе с app error: deadlock; recursive locking (ThreadError) появляется в выводе консоли сервера.

По (потенциально опасному) действию удаления Rack::Lock ошибка взаимоблокировки может быть устранена, но соединения по-прежнему зависают, хотя консоль сервера показывает, что соединения были приняты.

Неудивительно, что это не удается. Из сообщения об ошибке я думаю, что Unicorn несовместим из-за причин, связанных с его сетевой архитектурой (многопоточность / параллелизм). Но опять же, это может быть какая-то ошибка в этом конкретном промежуточном программном обеспечении Rack...

Кто-нибудь знает конкретную техническую причину, почему Unicorn несовместим?


ОРИГИНАЛЬНЫЙ ОТВЕТ:

Вы проверили порты как для веб-сервера, так и для сервера WebSocket и их журналы отладки? Эти сообщения об ошибках звучат так, будто они подключаются не к серверу WebSocket.

Ключевое различие между двумя веб-серверами, которые вы использовали, заключается в том, что один (Thin) основан на EventMachine, а другой (Unicorn) - нет. В вики проекта Websocket Rails говорится, что режим автономного сервера должен использоваться для веб-серверов, не основанных на EventMachine, таких как Unicorn (что потребует еще более сложной настройки Heroku, так как для этого требуется сервер Redis). Сообщение об ошибке RuntimeError (EventMachine not initialized: evma_install_oneshot_timer): предполагает, что автономный режим не использовался.

Heroku AFAIK предоставляет только один внутренний порт (предоставляемый в качестве переменной среды) внешне как порт 80. Серверу WebSocket обычно требуется собственный адрес сокета (номер порта) (который можно обойти путем обратного прокси-сервера WebSocket). Похоже, что Websocket-Rails обходит это ограничение, подключившись к существующему веб-серверу на основе EventMachine (который не предоставляет Unicorn), взломав Rack.

Другие вопросы по тегам