Статьи

Введение в Event Machine

Event Machine — это потрясающая библиотека, которая обеспечивает основанный на событиях неблокирующий ввод-вывод в Ruby.

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

Event Machine в основном работает с пулом потоков. Всякий раз, когда это необходимо, он использует некоторые потоки из пула и по завершении возвращает их обратно. Используя эти потоки, Event Machine упрощает разработку масштабируемых параллельных приложений.

В основном, в очень упрощенном (может быть, слишком упрощенном) выражении, это немного похоже на Node.js для Ruby.

Но с Node.js базовым языком является Javascript, который не имеет большого количества утилит по умолчанию для выполнения обычного старого (неосвященного) ввода-вывода, что означает, что его труднее запутать и написать неправильный тип код в неправильном месте.

Event Machine — это невероятно мощный инструмент для разработки мощных (например, в реальном времени) сервисов.

В любом месте, где нужно написать сервер для одновременной обработки множества соединений, записи в файловую систему и перехода на другие процессы или своевременного выпуска различных процессов, Event Machine отлично работает.

Итак, начнем с Event Machine.

Установка

Как мы все знаем, у Ruby есть замечательный RubyGems, который делает все невероятно простым. Просто подключите это к терминалу:

gem install eventmachine

view raw
gistfile1.sh
hosted with ❤ by GitHub

Если он задыхается от собственных расширений, убедитесь, что у вас установлены инструменты сборки (например, gcc), и прочитайте сообщение об ошибке, чтобы проверить, отсутствуют ли какие-либо необходимые библиотеки.

Эхо-сервер

Скромный эхо-сервер (сервер, который отправляет обратно тот же текст, который вы отправили ему), немного похож на «Hello, world!» Кода сокета — он ускоряет настройку.

Вот это с Event Machine:

require ‘eventmachine’
class Echo < EM::Connection
def receive_data(data)
send_data(data)
end
end
EM.run do
EM.start_server(«0.0.0.0», 1337, Echo)
end

view raw
gistfile1.rb
hosted with ❤ by GitHub

Это выглядит пугающе, но, будучи разбитым, вполне усваивается. Мы создаем класс Echo, который является дочерним элементом EM :: Connection. Затем мы переопределяем метод receive_data , который является методом обратного вызова . Методы обратного вызова вызываются, когда что-то происходит; в этом случае receive_data вызывается, когда сервер получает данные.

receive_data передается в данных в качестве аргумента, и затем мы используем метод send_data (часть EM :: Connection) для отправки этих данных клиенту через то же EM :: Connection.

Затем мы используем метод EM.run . Это запускает Event Machine, которая в основном является циклом, который запускает события. Требуется функциональный блок перед запуском. EM.start_server запускает сервер EventMachine, используя класс Echo в качестве EM :: Connection.

Чтобы проверить это, мы можем подключиться к localhost через порт 1337:

telnet 127.0.0.1 1337

view raw
gistfile1.sh
hosted with ❤ by GitHub

Напечатайте что-нибудь, нажмите Enter, и вы должны увидеть, что это эхо прямо на вас.

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

Сервер может обрабатывать несколько соединений одновременно!

Используя свой пул потоков, Event Machine волшебным образом сделала все одновременно.

Если вы написали многопоточный код, вы знаете, насколько это было сложно.

Существует много других методов, таких как receive_data для переопределения (каждый из которых запускается при возникновении определенных событий), основными из которых являются:

  • connection_completed — вызывается после завершения соединения
  • post_initpost_init до установления соединения
  • unbindunbind после отключения клиента

Они не очень сложны в использовании; в этом отношении документация Event Machine просто великолепна. Однако может быть довольно сложно понять, как работает модель реактора (то есть основанная на событиях), и именно здесь происходит большинство ошибок.

предосторожность

Вы никогда не должны иметь блокирующий код в обратных вызовах событий.

Код блокировки состоит из методов, которые могут не возвращаться немедленно; например, открытие и чтение файла является хорошим примером.

Если вы это сделаете, вы убиваете цель системы, основанной на событиях (часто называемой схемой реактора), потому что ваша программа будет ждать, когда что-то произойдет. Это именно то, чего мы пытаемся избежать, используя схему реактора.

Обычно для таких задач Event Machine имеет свою собственную неблокирующую версию, которая подключается к среде выполнения Event Machine и запускает событие, когда оно выполнено.

Таймеры

Event Machine также обрабатывает таймеры. Вы можете назначить обратные вызовы, чтобы установить временные рамки.

Вот пример:

require ‘eventmachine’
EM.run do
EM.add_periodic_timer(1) do
puts «time elapsed»
end
end

view raw
gistfile1.rb
hosted with ❤ by GitHub

На этот раз нам даже не нужен класс, потому что у нас нет связи. Код не требует пояснений: добавьте периодический таймер в цикл событий, и он запускает блок каждый раз, когда происходит событие. Блок просто выводит «прошедшее время» на экран.

Выполнение этого должно выдавать «истекшее время» каждую секунду.

Но что, если мы захотим остановить это через десять секунд? Вот код:

require ‘eventmachine’
EM.run do
EM.add_timer(10) do
puts «STOP»
EM.stop_event_loop
end
EM.add_periodic_timer(1) do
puts «time elapsed»
end
end

view raw
gistfile1.rb
hosted with ❤ by GitHub

На этот раз мы используем новое средство под названием add_timer. Вместо того, чтобы периодически запускать события по истечении времени, он запускает событие только один раз, то есть по истечении первых 10 секунд.

Отсрочка

Скажем, вы хотите сделать что-то на фоне EM; то, что не влияет на клиентов сервера сразу.

Для этого вы можете использовать отсрочку. С отсрочкой вы можете отодвинуть задачу в фоновый режим, чтобы она не влияла на работу Event Machine.

Пример прояснит это:

require ‘eventmachine’
EM.run do
EM.add_periodic_timer(1) do
puts «time elapsed»
end
EM.defer do
logs = IO.readlines(‘/var/log/kernel.log’)
end
end

view raw
gistfile1.txt
hosted with ❤ by GitHub

(Примечание: вам может потребоваться проверить путь /var/log/kernel.log в зависимости от вашей системы. Путь просто должен указывать на какой-то большой файл)

IO.readlines — это блокирующий вызов, т. Е. Нам нужно дождаться его окончания, прежде чем двигаться дальше. Но, используя EM.defer, мы можем сохранить наш периодический таймер таким, каким он должен быть.

Порожденные процессы

Порожденные процессы являются механизмами параллелизма, как и отложенные, но имеют некоторые особые качества.

Отсрочки запускаются, как только мы говорим «EM.defer block », с другой стороны, порожденные процессы зависают и ждут сообщения, а затем исполняются.

Пример:

require ‘eventmachine’
EM.run do
spawned_process = EM.spawn do |did_what|
puts «I just » + did_what
end
EM.add_periodic_timer(1) do
spawned_process.notify «ate»
end
EM.add_periodic_timer(2) do
spawned_process.notify «slept»
end
end

view raw
gistfile1.txt
hosted with ❤ by GitHub

Итак, порожденный процесс выполняется, когда он получает уведомление с помощью сообщения.

Вывод

Подумай об этом на секунду. Мы работаем с полным механизмом потока здесь. Передача сообщений, обработка таймеров, использование фоновых потоков, обработка нескольких соединений, и это так просто. Например, пример порожденного процесса занял бы дни работы в C — Event Machine, я аплодирую вам.

Я надеюсь, что вам понравилось узнавать о Event Machine и что эти знания пригодятся вам.

Я хотел бы услышать ваши мысли о статье в разделе комментариев ниже.