EventMachine и потоки Ruby — что здесь происходит на самом деле?

#ruby-on-rails #ruby #eventmachine

#ruby-на-рельсах #рубин #eventmachine #ruby-on-rails #ruby

Вопрос:

мы используем Rails и EventMachine вместе, и при использовании этой комбинации с Passenger необходимо выполнить некоторые очень специфические настройки. После множества проб и ошибок инициализация EventMachine работает хорошо, но я хотел бы немного лучше понять код. Как вы можете видеть ниже в этом фрагменте кода, наш инициализатор проверяет наличие Passenger, а затем проверяет, является ли это разветвленным процессом перед перезапуском EventMachine.

 if defined?(PhusionPassenger)
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
  # for passenger, we need to avoid orphaned threads
    if forked amp;amp; EM.reactor_running?
      EM.stop
    end
    Thread.new {
      EM.run do
  

Мой вопрос связан с EM.reactor_running? и команды EM.stop. Если Пассажир разветвил наш процесс, зачем мне перезапускать ссылку EM в новом потоке? Если EM.reactor_running? возвращает true, на какой экземпляр EM я ссылаюсь?

Вы можете увидеть полный код инициализатора в нашем блоге здесьhttp://www.hiringthing.com/2011/11/04/eventmachine-with-rails.html

Ответ №1:

Прежде всего, для каждого процесса Ruby существует только один экземпляр EventMachine, поэтому независимо от того, что вы всегда будете ссылаться на один и тот же экземпляр EM, независимо от потока, в котором вы находитесь в данный момент.

Вы запускаете реактор в новом отдельном потоке, чтобы он не блокировал основной поток (целью которого является обслуживание веб-запроса). В противном случае EM.run взял бы на себя управление, войдя в цикл выполнения, больше не покидая блок EM.run. EM.reactor_running? возвращает true, ну, если где-то выполняется EM-цикл. Поскольку для каждого процесса Ruby существует только один, методу достаточно просто определить, запущен ли он или нет.

Представленная здесь настройка — это самый простой способ использовать EM внутри обычного процесса Ruby, не вмешиваясь во все остальное, что запущено. Я предполагаю, что вы отправляете сообщения брокеру AMQP из своего веб-приложения. Всякий раз, когда вы отправляете сообщение, оно переходит в цикл выполнения EM в отдельном потоке, эта часть довольно прозрачна для вас и не влияет на основной цикл, который может продолжить обработку веб-запроса Rails. Однако будьте осторожны, чтобы всегда помещать что-либо в цикл EM, используя EM.next_tick. Попытка обработать сокеты, открытые EM в разных потоках, может привести к плохим событиям, которые я видел в производстве, между прочим, используя и создавая библиотеку под названием happening 😉

Остановка цикла EM перед запуском нового устраняет цикл EM, который может остаться от родительского процесса, что может привести к проблемам с файловыми дескрипторами, открытыми с использованием EM в родительском процессе. В пользовательском коде это можно обойти с помощью EM.fork_reactor , но поскольку родительский процесс находится вне вашего контроля, безопаснее проверить, существует ли реактор, и остановить его перед запуском нового экземпляра.

Комментарии:

1. Отличное описание, спасибо. Я четко понимаю необходимость запуска EM в своем собственном потоке, но когда вы говорите «EM цикл, который может остаться», вот тут я немного не совсем понимаю. Как происходит «оставление» EM-цикла? Я не закрываю родительский процесс, верно?

2. Это именно то, что ты делаешь. Вы закрываете потенциально оставшийся EM от родительского процесса. Если EM-цикл запускается в родительском процессе, он тоже будет разветвлен, но обычно вас совсем не интересуют файловые дескрипторы, оставшиеся от родительского процесса, поэтому вы начинаете с нуля. Учитывая, что Пассажир вряд ли сможет самостоятельно запустить электромагнитный цикл, это скорее мера безопасности, чем что-либо еще.

3. В аналогичном ключе я работаю с RabbitMQ через ruby-amqp gem в приложении websocket, работающем под управлением thin. У Thin есть собственный цикл EventMachine, и в настоящее время я использую EventMachine.next_tick для взлома и выполнения своих функций amqp. Правильно ли это или я должен использовать EventMachine.fork_reactor, чтобы предоставить AMQP его собственный EM для использования?

4. @wchrisjohnson Вы должны быть в порядке, чтобы использовать реактор, созданный Thin. fork_reactor создает новый процесс, который больше не будет иметь контекста тонкого веб-сервера, который, я полагаю, вам нужен для интеграции с WebSockets.

5. Спасибо — это именно тот случай.