Итак, EventMachine — быстрый и легкий фрэймворк для сетевого взаимодействия в Ruby. EventMachine используется событейно-ориентированный (асинхронный) механзим обработки сетевых соединений. (О различиях между синхронными и асинхронными моделями обработки сетевых соединений посвящено множество информации в сети).
Так как, в русскоязычном интернете очень скудная иноформация по этому замечательному gem'у выкладываю эту статью.
Установка достаточно стандарта для Ruby-программиста:
gem install eventmachine
Начнем с примера из документации (несколько измененного):
Теперь разберем по порядку (обратному — так будет удобнее):
EventMachine::run — инициализирует и запускает цикл обработки сообщений (EventReactor), возврат из этой функции происходит только при вызове метода stop_event_loop. Методу можно (и нужно) передать блок, который выполняется до запуска цикла обработки сообщения. Например, мы можем инициализировать здесь сервер, настроить таймеры, установить клиентское соединение и т.п. В данном же примере мы запускаем эхо-сервер, для этого служит метод (метод модуля EventMachine) EventMachine::start_server, его параметры это IP адрес и порт для прослушивания, в нашем примере IP пустой, чтобы можно было соединяться с любого хоста. Следующий и наверное самый важный параметр — это обработчик соединения — например, имя класса (подкласса EventMachine::Connection) или модуля (в этом случае модуль подмешивается к анонимному подклассу EventMachine::Connection). В этом примере это класс EchoServer.
На каждое соединение с сервером инициируется свой объект класса EchoServer!!!
В цикле обработке сообщений (EventReactor) инициируются сообщения — в данном контексте — это вызовы методов объекта класса EchoServer. Основными сообщениями являются:
В приведенном примере, после установки соединения с клиентом выводиться сообщение — «Соедиение с сервером», при обрыве «Соединение закрыто». При получение сообщения оно отправляется назад используя метод send_data (отправить сообщение), в случае если получено сообщение quit — соединение закрывается.
Более подробно, о методах класса смотрите в документации.
С клиентом все почти по аналогии — примеры можно найти в интернете в том числе и на русском языке.
Вообще проще всего можно протестировать используя telnet:
telnet localhost 11777
Мы же рассмотрим более интересные темы.
Я уже встречал на хабре чат сделанный с использованием EventMachine, однако там не использовались все его вкусности. Сейчас мы напишем простенький чат (не буду врать, но думаю что 1000 коннектов он точно выдержит, если чуть-чуть поправить код, в качестве подсказки — метод EventMachine.defer).
Рассмотрим каналы EventMachine::Channel. Этот механизм совсем не нов и используется в различных системах, более того это целый паттерн. Механизм следующий — есть канал (ну скажем длинный коридор этажа офисного здания), и есть подписчики (те кто открыл двери, чтобы слышать все что происходит в коридоре). Любой, даже не подписчик (тот кто не открывал дверь) может послать сообщение в канал (крикнуть в коридор все что угодно) и все подписчики его получат (услышат). От канала можно отписаться (закрыть дверь).
Итак, у канала есть три метода: subscribe (подписаться), push (послать сообщение), unsubscribe (отписаться). Ниже представлена реализация чата (с одним каналом — комнатой)
Вообще, программирование с использование EventMachine несколько сложнее обычного. Связано это с асинхронностью, использование большого количества блоков, процедур и прочего. В приведенных выше примерах этого почти не видно, но если вы захотите использоваться EventMachine вам придется с этим столкнуться.
Так как, в русскоязычном интернете очень скудная иноформация по этому замечательному gem'у выкладываю эту статью.
Установка достаточно стандарта для Ruby-программиста:
gem install eventmachine
Начнем с примера из документации (несколько измененного):
require 'rubygems'
require 'eventmachine'
class EchoServer < EventMachine::Connection
def post_init
puts "Соединение с сервером"
end
def receive_data data
send_data ">>> #{data}"
close_connection if data =~ /quit/i
end
def unbind
puts "Соединение закрыто"
end
end
EventMachine::run {
EventMachine::start_server '', 8081, EchoServer
}
Теперь разберем по порядку (обратному — так будет удобнее):
EventMachine::run — инициализирует и запускает цикл обработки сообщений (EventReactor), возврат из этой функции происходит только при вызове метода stop_event_loop. Методу можно (и нужно) передать блок, который выполняется до запуска цикла обработки сообщения. Например, мы можем инициализировать здесь сервер, настроить таймеры, установить клиентское соединение и т.п. В данном же примере мы запускаем эхо-сервер, для этого служит метод (метод модуля EventMachine) EventMachine::start_server, его параметры это IP адрес и порт для прослушивания, в нашем примере IP пустой, чтобы можно было соединяться с любого хоста. Следующий и наверное самый важный параметр — это обработчик соединения — например, имя класса (подкласса EventMachine::Connection) или модуля (в этом случае модуль подмешивается к анонимному подклассу EventMachine::Connection). В этом примере это класс EchoServer.
На каждое соединение с сервером инициируется свой объект класса EchoServer!!!
В цикле обработке сообщений (EventReactor) инициируются сообщения — в данном контексте — это вызовы методов объекта класса EchoServer. Основными сообщениями являются:
- post_init — вызывается после установки соединения
- unbind — вызывается при обрыве соединения
- receive_data — вызывается при получении сообщения из соединения
В приведенном примере, после установки соединения с клиентом выводиться сообщение — «Соедиение с сервером», при обрыве «Соединение закрыто». При получение сообщения оно отправляется назад используя метод send_data (отправить сообщение), в случае если получено сообщение quit — соединение закрывается.
Более подробно, о методах класса смотрите в документации.
С клиентом все почти по аналогии — примеры можно найти в интернете в том числе и на русском языке.
Вообще проще всего можно протестировать используя telnet:
telnet localhost 11777
Мы же рассмотрим более интересные темы.
Я уже встречал на хабре чат сделанный с использованием EventMachine, однако там не использовались все его вкусности. Сейчас мы напишем простенький чат (не буду врать, но думаю что 1000 коннектов он точно выдержит, если чуть-чуть поправить код, в качестве подсказки — метод EventMachine.defer).
Рассмотрим каналы EventMachine::Channel. Этот механизм совсем не нов и используется в различных системах, более того это целый паттерн. Механизм следующий — есть канал (ну скажем длинный коридор этажа офисного здания), и есть подписчики (те кто открыл двери, чтобы слышать все что происходит в коридоре). Любой, даже не подписчик (тот кто не открывал дверь) может послать сообщение в канал (крикнуть в коридор все что угодно) и все подписчики его получат (услышат). От канала можно отписаться (закрыть дверь).
Итак, у канала есть три метода: subscribe (подписаться), push (послать сообщение), unsubscribe (отписаться). Ниже представлена реализация чата (с одним каналом — комнатой)
require 'rubygems'
require 'eventmachine'
require 'socket'
class User < EventMachine::Connection
@@room = EventMachine::Channel.new
attr_reader :port, :ip, :sid
def receive_data data
@@room.push("#{ip}:#{port} >>> #{data}")
close_connection if data =~ /quit/i
end
def unbind
str = "Ушел #{ip}:#{port}\n"
@@room.push(str)
puts str
@@room.unsubscribe(sid)
end
def post_init
@port, @ip = Socket.unpack_sockaddr_in(get_peername)
str = "Пришел поговорить #{ip}:#{port}\n"
@sid = @@room.subscribe { |msg| send_data msg }
@@room.push(str)
puts str
end
end
EventMachine::run {
EventMachine::start_server '', 11777, User
}
Вообще, программирование с использование EventMachine несколько сложнее обычного. Связано это с асинхронностью, использование большого количества блоков, процедур и прочего. В приведенных выше примерах этого почти не видно, но если вы захотите использоваться EventMachine вам придется с этим столкнуться.