- PVSM.RU - https://www.pvsm.ru -
Привет!
Пару месяцев назад я опубликовал [1] на Хабре статью, посвященную описанию open-source проекта Centrifuge [2]. Напомню, что это сервер рассылки сообщений подключенным клиентам (в основном из веб-браузера) в реальном времени. Написан на Python.
С тех пор я продолжал работать над проектом в свободное время и сейчас готов поделиться накопившимися мыслями и изменениями.
Изначально, Центрифуга была самобытным проектом. Не сильно заботясь о воспроизведении функционала существующих аналогов, я писал код так, как казалось правильным мне самому. В итоге сообщения клиентам доставлялись, всё работало, но! Было ли удобно всем этим пользоваться? Нет!
В конце июня я наткнулся на великолепную статью от Serge Koval [3] — Python and real-time Web [4]. Удивительно, но на тот момент я не знал о существовании Faye [5]. Статья открыла мне этот замечательный проект, как и понимание того факта, что все-таки Центрифуга в своем текущем состоянии не сильно упрощает жизнь при разработке real-time веб-приложений.
С того времени я допиливал Центрифугу с прицелом на удобство использования и с оглядкой на pusher.com [6], pubnub.com [7] и Faye.
Вопрос, зачем мне нужно было писать код с нуля, если уже существуют более матерые и продвинутые аналоги, неизбежен. Вот несколько причин:
Теперь расскажу об изменениях, произошедших с момента написания предыдущей статьи о Центрифуге.
Во-первых, структура — проекты, пространства имен и их настройки — теперь по умолчанию будут храниться в SQLite — базе данных, входящей в стандартную библиотеку Python. Поэтому при запуске процессов Центрифуги на одной машине больше нет необходимости в установке PostgreSQL или MongoDB, как было ранее. Так как Центрифуга рассчитана на использование в небольших и средних проектах — я считаю, это важное и нужное изменение, так как одной машины должно хватить сполна.
Можно пойти чуть дальше — и запустить Центрифугу со структурой, описанной в конфигурационном файле. При этом теряется возможность динамически вносить и сохранять изменения из веб-интерфейса, но зато нет никаких зависимостей от внешнего хранилища. Данная возможность также чрезвычайно помогает при разработке.
Появилась поддержка presence и history — теперь можно узнать, кто в данный момент подключен к каналу, а также получить последние сообщения, отправленные в канал. Для хранения этих данных используется Redis. Если Redis не настроен — данные просто не будут доступны клиентам, ничего при этом не сломается.
Возникает вопрос. Сейчас Центрифуга использует ZeroMQ PUB/SUB сокеты для коммуникации между несколькими своими процессами. Быть может, раз в игру вступил Redis в качестве хранилища информации о подключенных клиентах и истории сообщений, то стоит использовать его PUB/SUB возможности и для коммуникации между процессами Центрифуги вместо ZeroMQ? В том единственном сравнительном бенчмарке [8], который я видел, ZeroMQ по производительности опережает Redis.
Поэтому на данный момент я оставил все как есть. Однако это спорный и важный момент.
Еще теперь можно получать сообщения о подключении(отключении) клиента к каналу (от канала). Приятный пустячок.
Наконец, самое, пожалуй, важное — появился javascript-клиент — обертка над протоколом Центрифуги. Он построен на основе Event Emitter [9], написанного Оливером Калдвеллом (Oliver Caldwell [10]). Теперь взаимодействовать с Центрифугой из браузера очень просто. Примерно вот так:
var centrifuge = new Centrifuge({
// настройки аутентификации
});
centrifuge.on('connect', function() {
// соединение с Центрифугой установлено
var subscription = centrifuge.subscribe('python:django', function(message) {
// функция, вызываемая при получении нового сообщения из канала
});
subscription.on(‘ready’, function() {
subscription.presence(function(message) {
// получена информация о подключенных к каналу клиентах
});
subscription.history(function(message) {
// история последних сообщений канала
});
subscription.on('join', function(message) {
// вызывается, когда новый клиент подключается к каналу
});
subscription.on('leave', function(message) {
// вызывается когда клиент отключается от канала
});
});
});
centrifuge.on('disconnect', function(){
// соединение с Центрифугой потеряно
});
centrifuge.connect();
За бортом в этом примере остались настройки аутентификации (о них можно прочитать в документации [11]). Также обратите внимание на название канала — оно состоит из имени пространства имен, которое должно быть создано в административном интерфейсе до подключения, в данном случае это python
. Непосредственно имя канала указывается после — в данном случае это django
. Пространство имен определяет настройки всех принадлежащих ему каналов. В настройках проекта можно выбрать пространство имен по умолчанию — тогда в javascript-коде можно не указывать явно название пространства имен. То есть, в случае если пространство имен python является дефолтным для проекта, можно писать вот так:
centrifuge.on('connect', function() {
var subscription = centrifuge.subscribe('django', function(message) {
console.log(message);
});
});
Авторизация в такого рода приложениях, пожалуй, самая сложная часть. Как я уже упоминал, в Faye нужно писать расширения на NodeJS или Ruby для защиты доступа к определенным каналам. Pusher.com для приватных каналов предлагает следующую схему:
При попытке подписаться на приватный канал, отправляется AJAX запрос на бэкенд вашего приложения с именем канала. В случае, если доступ разрешен, вы должны вернуть подписанный ответ, который в дальнейшем вместе с именем канала отправляется непосредственно в Pusher. Преимущество здесь в том, что ваше приложение на момент получения AJAX-запроса в большинстве случаев уже содержит объект текущего пользователя (например, в Django это request.user
).
В Центрифуге применяется немного иной подход. Идентификатор текущего пользователя отправляется один раз в момент подключения — его вы указываете при конфигурации javascript-клиента вместе с ID проекта и токеном. Токен — это HMAC [12], сгенерированный на основе секретного ключа проекта (о котором должен знать только бэкенд вашего приложения), ID проекта и ID пользователя. Токен необходим для проверки корректности переданных ID проекта и ID юзера. В дальнейшем при подписке на приватные каналы Центрифуга будет отправлять POST запрос вашему приложению со строковыми ID юзера, именем пространства имен и именем канала. Поэтому первым делом в функции-обработчике авторизации вам нужно будет получить объект своего пользователя по ID.
Еще один важный момент, касающийся авторизации — сейчас, чтобы подписаться на несколько каналов приходится несколько раз вызывать функцию subscribe
на клиентской стороне. Если каналы приватные, то каждая такая подписка будет приводить к POST запросу к вашему приложению. Не оптимизированное поведение, которое хотелось бы улучшить. Но тот же pusher.com, признавая, что такие случаи хоть и редки, но бывают среди требований их клиентов, пока в полной мере эту проблему не решил [13]. Здесь я пока в поиске правильного пути решения.
Хотелось бы отметить еще один способ защиты приватных данных, который никто не отменял. Например, чтобы сделать отдельные приватные каналы для каждого пользователя приложения — можно генерировать трудно угадываемые имена каналов на основе какого-либо секретного ключа и ID пользователя. В таком случае вполне можно обходиться без дополнительной авторизации, по крайней мере до тех пор, пока вашим клиентам не выгодно делиться именами своих приватных каналов:)
Есть возможность добавить кастомную асинхронную функцию (обрамленную tornado-декоратором @coroutine
) перед публикацией сообщения в канал. Внутри этой функции можно делать с сообщением все что угодно, в том числе вернуть None и тем самым отменить публикацию сообщения. Но, пожалуй, это мало кому пригодится, как и аналогичная возможность добавить обработчик, вызываемый после публикации. Это достаточно низкоуровневое вмешательство и требует знания Python и Tornado.
Установка Центрифуги в самом простом случае сводится к одной команде pip install centrifuge
внутри virtualenv. Однако на машине должен быть установлен ZeroMQ (libzmq3) и dev-пакет для PostgreSQL (сам PostgreSQL сервер необязателен). Найденные проблемы, которые могут возникнуть при установке из PYPI, и способ их решения описаны в документации [14]. Запуск одного процесса выполняется командой centrifuge
. Однако для запуска в боевую среду потребуется конфигурационный файл, так как в нем содержатся важные настройки безопасности. Также не обойтись без использования дополнительных опций командной строки, если вы хотите запустить несколько процессов.
Вот в этом разделе документации [15] я постарался как можно подробней объяснить как работает Центрифуга, какие есть опции запуска, какой адрес указывать при подключении из браузера и многое другое. На английском, правда.
Нагрузочное тестирование пока не проводил. Надеюсь, займусь бенчмарками в ближайшее время. Интересно сравнить с Faye, интересно запустить на PYPY. Ну и, конечно, необходимо продолжать работу над устойчивостью к всевозможным ошибкам, совершенствовать Python-код и javascript-клиент и так далее. Присоединяйтесь!
Спасибо за внимание!
Автор: FZambia
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/44025
Ссылки в тексте:
[1] опубликовал: http://habrahabr.ru/post/184262/
[2] Centrifuge: https://github.com/FZambia/centrifuge
[3] Serge Koval: https://github.com/mrjoes
[4] Python and real-time Web: http://mrjoes.github.io/2013/06/21/python-realtime.html
[5] Faye: http://faye.jcoglan.com/
[6] pusher.com: http://pusher.com
[7] pubnub.com: http://pubnub.com
[8] единственном сравнительном бенчмарке: http://blog.jupo.org/2013/02/23/a-tale-of-two-queues/
[9] Event Emitter: https://github.com/Wolfy87/EventEmitter
[10] Oliver Caldwell: https://github.com/Wolfy87
[11] прочитать в документации: https://centrifuge.readthedocs.org/en/latest/content/client_api.html
[12] HMAC: http://docs.python.org/2/library/hmac.html
[13] в полной мере эту проблему не решил: http://pusher.tenderapp.com/discussions/requests/13-connect-to-and-auth-with-multiple-channels-at-once
[14] описаны в документации: https://centrifuge.readthedocs.org/en/latest/content/install.html#install-centrifuge
[15] Вот в этом разделе документации: https://centrifuge.readthedocs.org/en/latest/content/overview.html#how-it-works
[16] Источник: http://habrahabr.ru/post/194640/
Нажмите здесь для печати.