Веб-сокеты не работают в моем приложении 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.