Ruby и EventMachine из песочницы

Ruby*
Итак, EventMachine — быстрый и легкий фрэймворк для сетевого взаимодействия в Ruby. 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 вам придется с этим столкнуться.
+25
25 октября 2010, 07:59
36

комментарии (10)

0
alno #
Подсветите код чем-нибудь и сделайте отступы, а то он как-то плохо читается
+2
andoriyu #
Офигеть! Вот эта статья! Да вы просто гений! Вы же перевели только, что один из 1000000 туторов по EM! Так держать! Конечно не надо рассказывать про callbacks, defer, deferable, et cetera. И о том как EM работает с jRuby, MacRuby, Rubinius. И про стандартные классы тоже не надо рассказывать, о том как добиться асинхронности тоже не надо говорить.
+1
kirill_x83 #
Спасибо конечно :))). Но это только начало. А насчет, EM c jRuby, MacRuby, Rubinius затрагивать не буду
0
Xarakternik #
Товарищ andoriyu язвит…
0
andoriyu #
> jRuby, MacRuby, Rubinius затрагивать не буду

Это вы зря… Уж про то как в jRuby работает это затронуть надо.

0
Goganchic #
А в чем принципиальное отличие кроме того, что jRuby использует OS threads а не green threads? Кроме того, что это повлияет на EM.defer еще какие-то отличия есть?
0
sha1dy #
поддерживаю! нафига упоминать, что при работе с EM надо использовать только совместимый API.
+1
akzhan #
Последнее время вместо EM используют связки из классических Rails- приложений вместе с демонами, написанными на node.JS. Большая часть сообщества node.JS — это программисты, использующие ruby ежедневно.
0
Chikey #
желательно чуть подробней — что используется вкупе с nodejs? Какие связки?
0
akzhan #
Event Machine обычно используется не для классических Web-сайтов, а для Web-сервисов, которые используются совместно с остальным функционалом сайта.

и в последнее время наметилась тенденция переписывания и написания таких сервисов именно на Node.JS, в то время как остальная часть сайта делается на Ruby/Rails.

Обычно для взаимодействия сервисов используется либо REST, либо база данных (CouchDB, Riak, MySQL etc.).

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.