Используем Nginx как “long polling” (Comet) сервер

Пётр Невенчанный 2011 M05 12
1197
0
0
0

Принцип “Long Polling” (или “Comet“) позволяет сделать возможным установление постоянного соединения клиента с Web приложением и также периодичной отправки данных клиенту в рамках этого соединения...

Принцип “Long Polling” (или “Comet“) позволяет сделать возможным установление постоянного соединения клиента с Web приложением и также периодичной отправки данных клиенту в рамках этого соединения (без необходимости клиента постоянно делать HTTP запросы для проверки новых данных). Другими словами, эта модель позволяет реализовать постоянные HTTP соединения для получения данных порциями. Наиболее популярное применение - чаты, твиттеры и другие live-updated потоки - клиент постоянно “слушает” сервер на предмет появления новых сообщений, и как только новые сообщения появляются - они мгновенно доставляются ему.

Сам принцип не несет в себе инноваций в HTTP протоколе, т.к. этот подход реализуем обычными средствами. Проблема заключается в том, что стандартное решение - установка постоянного соединения с обычным Web сервером (а значит и с обслуживающей платформой, например php-бекендом) - крайне ресурсоемкое и не применимо на практике. Другой подход - постоянно опрашивать приложение на предмет появления новых данных является еще более ресурсоемким.

Решением этой проблеммы стали т.н. HTTP-PUSH (или comet) сервера, позволяющие облуживать огромное количество постоянных соединений эффективно расходуя ресурсы. Как они устроены и как применяются на практике?

Принцип работы Comet-сервера

Необходимо отметить, что Comet сервер решает задачу отправки клиенту определенных данных порциями. Comet сервера реализуют т.н. HTTP Push Relay протокол (например, Basic HTTP Push Relay Protocol). Принцип работы такого протокола заключается в следующем:

  • На Web сервере создается т.н. “канал” с уникальным ключем. Это делается на стороне приложения путем посылки запроса к Comet-серверу
  • Клиент устанавливает обычное HTTP соединение с Comet-сервером, передавая в параметры ключ канала, из которого он будет получать данные. Сервер удерживает такое соединение, пока не отправит очередную порцию данных
  • Основное приложение, по определенному событию (например, кто-то написал сообщение клиенту), шлет в канал Comet сервера с нужным ключем это сообщение
  • Как только в канале появляется сообщение, Comet сервер отправляет его соответствующему клиенту и закрывает HTTP соединение (иногда соединения не закрываются, а продолжают быть активными)

Таким образом, сервер реализует “стэки” сообещений, а их раздача происходит без участия тяжелых бекендов. Бекенды вступают в силу только тогда, когда необходимо отправить сообщение.

Nginx и модуль HTTP Push

Модуль nginx_http_push_module позволяет превратить Nginx в Comet сервер, реализуя протокол Basic HTTP Push Relay Protocol. Преимущества этого модуля и протокола - в его простоте по сравнению с альтернативными решениями (например, протоколом Bayeux и сервером CometD).

Установка

Для установки модуля необходимо скачать его исходники и перекомпилировать Nginx, т.к. модуль является внешней разработкой:

wget http://pushmodule.slact.net/downloads/nginx_http_push_module-0.692.tar.gz
tar -xvf nginx_http_push_module-0.692.tar.gz
...
cd /where/nginx/sources/are
./configure \
  --add-module=/path/to/nginx/modules/sources/nginx_http_push_module-0.692/
make; make install

Конфигурация

Для базовой конфигурации нам необходимо объявить две рабочих точки в Nginx’e: точка публикации сообщений (приватная - доступна только для самого приложения) и точка раздачи сообщений (публичная - к которой будут подключаться клиенты):

	location /publish {
		# Название переменной с идентификатором канала
		# в нашем примере "cid", т.е. запрос будет таким:
		# http://example.com/publish?cid=s42378fwe
		set $push_channel_id $arg_cid;
		push_publisher;

		# Отключаем хранение очереди (сообщение удаляется после доставки)
		push_store_messages off;
	}

	location /listen {
		push_subscriber;

		# Обслуживать только первого "слушателя"
		# Остальным отправляем 403
		push_subscriber_concurrency first;

		# Идентификатор канала
		set $push_channel_id $arg_cid;

		# Тип ответа
		default_type text/plain;
	}

После этого при отправке сообщения нужно будет посылать его в соотв. канал, делая POST запрос на “/publish”. Клиент же отправляет GET запрос на “/listen” и ждет ответа. Когда приходит ответ, клиент делает повторный запрос на “/listen” и т.д.

Все параметры конфигурации с подробным описанием смотрите на официальном сайте.

Пример

В качестве примера ниже приведен код на PHP, который используется для отправки сообщения в канал:

# Определяем ID канала
$channel_id = 12345;

# Сообщение
$message = 'Привет тебе!';

# Отправляем сообщение в канал
$c = curl_init( 'http://localhost/publish?cid=' . $channel_id );
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, json_encode($message));
$r = curl_exec($c);

Для получения сообщения код на Javascript будет выглядеть где-то так (на основе Jquery):

var channelId = 12345;
function check_messages() {
	$.get('/listen?cid=' + channelId, {}, function(r) {
		// Считаем, что у нас есть div c id=messages,
		// куда мы дописываем сообщения
		$('#messages').append(r);
		setTimeout(check_messages, 500);
	}, 'json');
}

check_messages();

В каких случаях Вам приходилось пользоваться comet-серверами?

Источник: Highload

Оцените пост

0