Как работает PubSubHubbub Почти каждая длительная поездка с детьми проходит по одному сценарию. Ребенок постоянно спрашивает с заднего сиденья: "Мы уже приехали? Мы уже приехали? Мы уже приехали?", пока один из раздраженных родителей не прикрикнет: "Хватит спрашивать, я скажу, когда мы будем на месте!" Это смешная сценка, но когда видишь, как часто при работе в Web используется такой же подход нетерпеливого ребенка, становится не смешно. Возможно, вы пользуетесь какой-либо программой чтения фидов для отслеживания нового содержимого на любимых сайтах, будь то новости, блоги или журналы, такие как IBM® developerWorks. Программа чтения обычно работает по принципу опроса сайта, запрашивая URL фида (например, каждые 15 минут) для проверки появления нового содержимого. Если сайт и ваша программа чтения используют все возможности HTTP-протокола (в частности, заголовки Cache-Control), сетевой трафик при отсутствии изменений фида минимален. Но даже минимальное использование ресурсов со временем растет, особенно если представить себе, сколько людей интересуется тем же сайтом, что и вы. Весь этот необязательный трафик является реальной проблемой, поэтому несколько лет назад инженеры Google решили возродить классическую схему публикация/подписка. Публикация/подписка напоминает родителя в автомобиле, который говорит ребенку: "Скажи, какие места тебя интересуют, и я дам знать, когда мы там будем", а ребенок больше не должен снова и снова спрашивать: "Мы еще не приехали?" В данном случае вы подписываетесь на интересующие вас фиды, а издатель фида уведомляет вас об изменениях, поэтому опрос больше не нужен. Схема публикация/подписка используется довольно давно, особенно в закрытых системах, но открытая природа Web затрудняет ее полезную и безопасную реализацию. Существуют и более старые решения, например, RSSCloud (см. раздел Ресурсы), но пришло время для появления нового поколения схемы публикация/подписка для Web. Результат был назван причудливым именем PubSubHubbub, которое часто сокращается до PuSH. Возможно, PuSH является детищем нескольких сотрудников Google, но изначально это была открытая спецификация, над которой работало много участников, не имевших отношения к Google. Более того, спецификация разрабатывалась совместно с рекомендованной реализацией с открытыми исходными кодами. Существуют также и другие реализации, но разработчикам всегда приятно поучаствовать в проекте с открытыми исходными кодами, чтобы помочь развитию сообщества. В данной статье я представлю PuSH и расскажу, как начать использовать рекомендованную реализацию с открытыми исходными кодами. Квинтэссенция PubSubHubbub Полезно мысленно разбить PuSH на три фазы: обнаружение (Discovery), подписка (Subscribing) и публикация (Publishing). Это даст представление о самых главных частях PuSH, хотя и не обо всех (PuSH имеет, например, продуманный механизм отказа от подписки, который практически является самостоятельной фазой). Обнаружение Фаза обнаружения начинается с появления у подписчика интереса к какой-либо теме. Тема в PuSH представляет собой URL, который как правило можно представить себе в виде URL фида. Каждый раздел IBM developerWorks имеет свой собственный RSS-фид, и каждый из них может быть темой PuSH, где главный URL – это тот же URL фида, который используют для опроса клиенты, не работающие с PuSH. Важнейшее усовершенствование PuSH состоит в том, что он позволяет подписчику работать на системе, отличной от той, которая управляет подписками PuSH. Это может быть даже другой сервер. Это дает возможность пользователям передавать управление отельными частями протокола специализированной библиотеке или даже Web-сервису. В большинстве описаний PuSH говорится о подписчике как о единой сущности, но я считаю, что протокол определяет две отдельные сущности: одну я называю агентом подписчика, а вторую – собственно подписчиком. В некоторых случаях они будут реализовываться совместно, но это не обязательно. На рисунке 1 представлена диаграмма процесса обнаружения. Рисунок 1. Процесс обнаружения в PuSH Агент подписчика запрашивает фид от имени подписчика и ищет внутри него одну или несколько специальных ссылок на PuSH-хаб. Ниже приведен пример такой ссылки: Code: <link rel="hub" href="http://pubsubhubbub.appspot.com"/> В данном случае фид сообщает: "Я публикую на PuSH-хаб по адресу http://pubsubhubbub.appspot.com". Так случилось, что это Google-ссылка на PuSH-хаб, работающий на системе Google App Engine. При желании вы можете использовать любой хаб, включая и свой собственный, организованный с использованием Google-реализации с открытыми исходными кодами. Если такая ссылка на PuSH-хаб отсутствует, агенту подписчика больше нечего делать, и подписчик должен будет прибегнуть к опросу или какому-то другому механизму. Если такая ссылка присутствует, следующей фазой станет подключение подписчика к хабу для начала процедуры подписки. Подписка На рисунке 2 представлена диаграмма процесса подписки. Агент отправляет на хаб HTTP-запрос POST с информацией о процедуре обратного вызова – URL-адрес, по которому хаб должен отправлять уведомления о новом содержимом и о теме, интересующей подписчика. Имеется также механизм защиты от вредоносного ПО, притворяющегося агентом подписчика и подписчиком на ненужные фиды. Рисунок 2. Процесс подписки в PuSH После завершения подписки хаб добавит URL процедуры обратного вызова в свой список агентов подписчика для уведомления о наличии каких-либо изменений в содержимом. Публикация На рисунке 3 представлена диаграмма процесса публикации. При обновлении темы каждому из ее хабов посылается HTTP-запрос POST с URL-адресом измененного содержимого. В спецификации эта процедура называется New Content Notification (уведомление о новом содержимом). Затем каждый хаб отправляет запрос GET на получение нового содержимого темы; эта процедура называется Content Fetch (извлечение содержимого). Затем хаб отправляет обновленное содержимое каждому подписчику посредством HTTP-запроса POST; этот процесс называется Content Distribution (распределение содержимого). Рисунок 3. Процесс публикации в PuSH Возможности PuSH Это похоже на замысловатый танец, но в обмен на некоторое усложнение PuSH обеспечивает хорошую децентрализацию, а следовательно масштабируемость и гибкость. На рисунке 4 показано, насколько гибкими могут быть взаимодействия между несколькими хабами, издателями, подписчиками и агентами подписчиков в PuSH-сети. Рисунок 4. Взаимодействия нескольких подписчиков, агентов подписчиков, хабов и издателей "Покажите код" Теперь, после изучения основ протокола, я покажу, как использовать рекомендованную Google-реализацию PuSH с открытыми исходными кодами (лицензия Apache). В частности, я сконцентрируюсь на реализации подписчика, позволяющую настраивать системы, выполняющие роль подписчика. Загрузите исходный код из Subversion-репозитория проекта (см. Ресурсы) и ознакомьтесь с ним. Каталог subscriber – это готовое приложение App Engine. В нем нет того, что я называю агентом подписчика, поэтому вы должны будете создать его самостоятельно. Я включил в статью PHP-код, который поможет начать работать с каталогом subscriber_client. Подписчик не может находиться за сетевым экраном, если не выполнить дополнительную настройку маршрутизатора на разрешение работы с внешней сетью. Подписчика можно развернуть в вашей учетной записи App Engine с помощью Google App Engine SDK (см. Ресурсы) либо можно использовать SDK для размещения тестовой версии исходного кода на любой платформе Linux. IBM предоставляет инструментальные средства поддержки App Engine (см. Ресурсы) для переноса приложений в ПО промежуточного уровня, но они требуют наличия Java™, тогда как рекомендованная реализация PubSubHubbub написана на Python. Я продемонстрирую вариант использования SDK для размещения приложения на Linux®-системе. В этом случае у нас не будет таких возможностей масштабирования, как при использовании хранилища данных Google, но для реализации простого подписчика она все равно не понадобится. Реализация показана в листинге 1. Листинг 1. Настройка PubSubHubbub Code: #Настройка App engine SDK mkdir -p $HOME/.local/gae cd $HOME/.local/gae #Использование wget или curl -O wget http://googleappengine.googlecode.com/files/google_appengine_1.6.2.zip unzip google_appengine_1.6.2.zip #Настройка PubSubHubbub app svn checkout http://pubsubhubbub.googlecode.com/svn/trunk/ pubsubhubbub-read-only google_appengine/dev_appserver.py --address=$ADDR pubsubhubbub-read-only/subscriber/ Эта последняя строка запускает сервер на порту 8080. Не забудьте установить ADDR в имя хоста или IP-адрес сервера. При запуске версии Python старше 2.5 может появиться предупреждение, но его можно проигнорировать. Я упоминал, что вам нужно создать агент подписчика самостоятельно. Прелесть простого HTTP состоит в том, что ему достаточно единственной команды cURL, чтобы запустить агент подписчика и подписать ваш сервер на фид. Команды приведены в листинге 2. Листинг 2. Подписка при помощи cURL Code: curl -v http://pubsubhubbub.appspot.com/subscribe \ -d hub.callback=http://$ADDR:8080/subscriber\&\ hub.topic=http://stackoverflow.com/feeds/tag/python\&\ hub.verify=sync\&hub.mode=subscribe\&hub.verify_token=\&hub.secret= Команда cURL в листинге 2 связывается с хабом Google и подписывает вас на тему с Web-фида Stack Overflow. Stack Overflow – это сайт сообщества, на котором разработчики могут задавать вопросы и обсуждать проблемы. Я случайно узнал, что Stack Overflow использует хаб Google, поэтому не беспокоюсь за фазу обнаружения и просто выбираю фид на тему Python, которая очень активна. Если все идет нормально, вы получите ответ HTTP 204 от cURL, а также увидите отладочную информацию в консоли подписчика при подключении его к хабу. Немного подождав обновления фида Python Stack Overflow, вы увидите, что содержимое подписчика обновилось. Простую структуру обновлений от хаба можно увидеть, повторно выполнив cURL, например: curl http://addr:8080/items. addr следует заменить на ваш сервер подписчика, т.е. на значение, присвоенное переменной окружения ADDR. В моем случае примерно через час я заметил отправленные подписчику данные, которые выглядят так, как показано в листинге 3. Листинг 3. Полученные данные Code: [{"content": "...", "source": "http://stackoverflow.com/questions/9155264/xyz-question", "title": "XYZ Question", "time": "2012-01-25 05:11:22.849931"}] На месте многоточия находилось HTML-представление данных. С этой структурой очень легко работать на Python, а также ее можно преобразовать в JSON. И, конечно же, можно выполнить операцию публикации/подписки. Заключение Поначалу PubSubHubbub выглядит не самым простым в изучении протоколом, но, потратив немного времени на его освоение, вы увидите, насколько мощным он является и насколько полезным он может быть для разработчика ПО с открытыми исходными кодами, обменивающегося идеями и кодом в Web. Рекомендованная Google-реализация с открытыми исходными кодами уже имеет несколько версий, разработанных другими пользователями (в том числе коммерческими организациями) и являющихся основой для дополнительных реализаций PuSH. На данный момент существуют реализации хабов и подписчиков для многих языков и платформ, а если вы не хотите сами выполнять код, можно воспользоваться Web-сервисами. Вы даже не заметите, как начнете использовать публикацию/подписку для удовольствия и даже для получения прибыли. 12.07.2012 Юч Огбуджи, Главный консультант, Fourthought http://www.ibm.com/developerworks/ru/library/os-pubsubhubbub/