Главная > Open Source, PHP, web2.0, Высокопроизводительная архитектура > pushBridge.IO — Единый API на РНР для всех облачных push-сервисов

pushBridge.IO — Единый API на РНР для всех облачных push-сервисов

15 января 2012

Приветствую всех читателей. Сейчас в веб-разработках столько трендов, что не уследишь. Но вопрос о реал-тайм взаимодействии с пользователями сайта стоит остро прочти для любого проекта. Простейший способ - поставить один из широко доступных открытых comet-серверов, например, Dklab_Realplexor, Socket.IO или Faye - что кому по душе или в зависимости от стека технологий. Правда это путь достаточно сложных проектов, где команда может себе позволить такое решение.

Для многих проектов попроще (хотя это всегда вопрос конкретики приложения) логично будет использовать сторонние решения. А проще - арендовать как услугу функционал comet-сервера. Сегодня недостатка в таких сервисах нет, так что нам есть что обозревать.

И так, сначала давайте кратко ознакомимся с существующими push-сервисами, которые позволят нам без создания и поддержки своей серверной инфраструктуры поддерживать реал-тайм общение между клиентами проекта.

Таких сервисов всего 6: Pusher, Pubnub, Partcl, BeaconPush, X-Stream.ly и ioBridge (с некоторыми особенностями). Под катом - кратки обзор всех сервисов, особенностей РНР-библиотек для них и описание библиотеки pushBridge.IO для унификации работы со всеми облачными пуш-сервисами.


Pusher.com

Самый известный из подобных сервисов. Одновременно с этим и один из самых сложных из-за обилия возможностей. В общих чертах, коммуникация поделена на каналы, внутри которых есть пользовательские события, на которые подписывается клиентский код (javascript). Кроме этого есть ряд системных событий, а также система статусов и оповещений и подключениях/отключениях других пользователей канала. Возможна и работа с приватными каналами, так, что даже подключенный пользователь, но не знающий кодового идентификатора, не будет получать события.

Для взаимодействия с другими системами, Pusher использует REST-HTTP-API (впрочем, как и все подобные сервисы), и предоставляет ряд готовых библиотек для 11 языков, включая Clojure, Groovy и ColdFusion. Меня интересовал только РНР API, который представлен 4-мя плагинами - для Kohana, Code Igniter и бандл для Simfony 2 фреймворков, а также одним независимым классом (http://github.com/squeeks/Pusher-PHP). Для работы он требует расширения cURL и json, а также sha256 в качестве алгоритма хеширования.

В качестве примечания скажу, что именно с этого сервиса возникла идея моей библиотеки. Буду честен - для одного проекта мне понадобился реалтайм, а в тестовой версии писать свой сервер не хотелось, поэтому за пару минут я думал подключить пушер, тем более, аккаунт у меня уже был. оказалось, что при всех достоинствах и обилии документации, система не такая простая, и не все моменты там удачно расписаны. очень помог режим дебага, когда на сайте в разделе консоли выводятся в реальном времени все ваши запросы, отправляемые через API. Но где-то я ступил и хотя запросы посылались, до клиента они не доходили. На этом месте, после часа отладки я забросил это дело. Перспектива снова переписывать код мне совсем не нравилась, к тому же, в финальной версии моего проекта все равно пришлось бы переходить на собственный сервер. Так и возникла идея - а зачем каждый раз все переписывать, если можно попробовать свести все в один унифицированный интерфейс. Забегая вперед, скажу, что идея успешно нашла свою реализацию!

PubNub.com

Этот сервис проще и, в этом, я вижу его преимущество. API предельно прост - есть каналы, в них сообщения и все. Формат данных, конечно, везде JSON. Как в клиентской части, так и на серверной стороне, Pubnub имеет самое широкое покрытие, предоставляя доступ к сервису на любой платформе и языке программирования. Простота и мощная поддержка всех платформ делает этот сервис самым интересным из рассматриваемых, особенно, если вам надо обеспечить реалтайм везде и всюду.

PHP библиотека для доступа к сервису достаточно простая, позволяет как отправлять, так и получать сообщения, базируясь на cURL. Работает как на 5.2, так и на 5.3 версии РНР, позволяю использовать плюшки вроде callback функций. После исследования исходного кода остался большой вопрос - почему-то при отправке, после преобразования в JSON, длина сообщения не должна превышать 1800 символов! С чем связанно это ограничение пока не ясно, буду связываться с разработчиками и выяснять.

Partcl.com

Достаточно новый сервис на этом рынке, первоначально ориентирован на встраивание обновляемых в реальном времени тегов на веб-странице. В настоящее время API там достаточно расширен и позволяет получать историю изменения значений, строить графики, также работающие в реальном времени и т.п. В отличие от остальных сервисов, позиционируемых лишь как коммуникационные, Partcl работает и как контент-провайдер, сохраняя все сообщения и данные, что достаточно уникальная функция. Раскрывая некоторые подробности, скажу, что серверная часть написана на Node.JS+Redis (именно реал-тайм “раздавалка” - я ее непосредственный разработчик), а веб-часть на Zend Framework.

Хотя для системы уже был написан API на РНР, и даже плагин для интеграции в WordPress, он, по моим меркам, был достаточно примитивный и вполне мог не работать на некоторых хостингах. Так что я попытался еще и написано новую реализацию библиотеки для partcl-а. Получилось даже лучше, чем оригинальная.

BeaconPush.com

Малоизвестный сервис, однако единственный, который предлагает выделенный сервер для требовательных клиентов (система, кстати, написана на Java). А так, он обычный, правда кроме каналов и сообщений выводит наверх еще и абстракцию пользователя, упрощая коммуникацию именно между конкретными подключенными клиентами. Кроме этого, интересный функционал веб-хуков, когда сервис сам дергает указанный вами URL, сигнализируя про вход/выход пользователей из канала. Также есть возможность управлять возможностями публикаций - если не включить ее, любой, зная публичный ид, сможет публиковать данные, иначе только имея секретный ключ.

А вот с РНР API у этого сервиса никак не сложилось. Он, конечно, есть (https://github.com/ImDom/BeaconPush-PHP и даже модуль для Code Igniter-а), но качество... очень далекое от хорошего. Кстати, автор модуля зашил в исходник свой аккаунт. Честно попытавшись использовать готовый модуль, но так и не смог его настроить на правильную работу (тупо отказывался принимать мой ид аккаунта), я полностью переписал его API под Zend_Http.

X-Stream.ly

Еще один почти неизвестный сервис, однако обладающий рядом интересных фишек. Например, можно создавать специально ключи для регламентирования доступа конкретных юзеров к API (видимо из-за бизнес модель). Также, он единственный, кроме Partcl-а, обладает функционалом постоянного хранения сообщений. Другие возможности более-менее стандартные - каналы, события, приватные каналы с доступом по паролю, статусы пользователей. Уникальной фичей является твиттер-фид, когда сервис сам будет подключаться к указанному Twitter-аккаунту и транслировать новые сообщения всем подключенным пользователям. На событие появления сообщения в канала можно поставить калбеки, которыми также можно управлять через REST-API. Кстати, этот сервис единственный, кто для публикации (и вообще, доступа) к своему HTTP API, кроме ид и секретного ключа, требует и HTTP-авторизацию, используя логин и пароль вашего аккаунта, а также работу только через SSL-соединение, что вносит дополнительные требования к хостингу.

Родной серверный API у сервиса более чем скудный - C#, Ruby и Node.JS. Пришлось с нуля реализовать часть их API, так что здесь я первый, кто написал библиотеку.

ioBridge.com

Самый интересный и странный сервис, не совсем даже push. Он ориентирован на подключение к вебу всяких железок (контроллеров и плат), а также обеспечивает отображение данных, сбор и хранение, ну и управление через веб. Родного клиента на РНР также нет, в официальном форуме есть несколько предложений и набросков кода, однако они далеки от качественной реализации. Да и я понял почему, начав сам его делать - сервис местами очень уж странный, например, чтобы получить actionId виджета и ид сессии, надо сделать запрос к их серверу, который сразу возвращает кусок HTML+JavaScript, из которого тупым парсингом строк приходиться выуживать данные, которые потом нужны для подключения и отправки команд. К сожалению, дальше базового кода я не смог продвинуться, видимо, для полноценного тестирования надо и само устройство какое-нибудь подключить. Так что этот код не протестирован в реальных условиях, если кто имеет опыт работы с сервисом, буду благодарен за подсказки и посильную помощь.

А теперь о самом главном - pushBridge.IO PHP Library

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

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

Библиотека разделена на две части - базовый класс отвечает за инициализацию нужного адаптера, обрабатывает сообщение, при необходимости, сериализирует его. Для этого я использовал класс Zend_Serializer, таким образом, поддерживаются все его форматы - от обычного Json до экзотического Wddx.

За работу с конкретным сервисов отвечает класс адаптера, который, при необходимости, подключает родную библиотеку. Пока родные классы используются только для Pusher-а и PubNub-а, ввиду того, что сервисы предоставляют свои специфические функции, которые пока не перенесены. Зато после инициализации вы всегда можете продолжить работать напрямую, просто вызвав метод getConnection().

Я так же попытался как-то унифицировать данные для авторизации. Одни сервисы используют app_id и секретный ключ, другие именуют их publish и subscribe/public ключами и т.п. Потому я попытался свести их к минимальному множеству и сохранить одно наименование для всех сервисов:

  • appId - обычно это ид приложения или API-key, уникальный идентификатор аккаунта или приложения
  • authKey - секретный ключ, дающий право на публикацию данных
  • secretKey - пароль или другой секретный ключ для доступа к аккаунту
  • readKey - публичный ключ для подключения в качестве клиента и чтения данных
  • emailKey - логин пользователя в виде e-mail (пока это исключительно для x-Stream.ly)

Для работы с библиотекой достаточно трех строк:

1. Инициализация и подключение.

$push = new pushBridge_IO( [ Adapter ], [serializer] );

Первым параметром идет экземпляр адаптера для нужного вам сервиса, например:

 

new pushBridge_Adapter_Pusher(Array('appId' => 'Your app Id', 'authKey' => 'Your key', 'secretKey' => 'Your secret key', 'debug' => true));

 

Обычно, достаточно только данных для авторизации, набор которых специфичен для сервиса. Но библиотека, в отличие от родных классов, позволяет гибко управлять методами подключения, используя сетевой стек от Zend_Http. Для запросов можно использовать cURL, напрямую сокеты или адаптер для работы через прокси. По умолчанию используется cURL, но если вам надо, передайте в качестве параметра httpAdapter название нужно адаптера, а в httpAdapterConfig - любые его настройки согласно документации по ZF (например, вот здесь). Каюсь, в этом моменте есть еще баг в текущей реализации библиотеки, пока опции, заданные пользователем для адаптера, не используются, это будет исправлено в следующей версии (на днях).

Так как разные сервисы требуют (или наоборот) разные методы, можно задать в параметре method каким образом адаптер будет подключаться к серверу. Обычно это GET, но иногда сервисы понимают только POST. Адаптер это учитывает, но вы можете переопределить этот параметр.

Библиотека вставляет свой кастомный хедер в запрос, X-Powered-By, идентифицируя себя, при желании это можно отключить для экономии сетевого трафика или просто из принципа.

Второй параметр - сериализатор, который будет обрабатывать сообщение. Все сервисы принимают или JSON, или (некоторые, например, Partcl) просто строку, без преобразования. Изначально там json, используя класс Zend_Serializer, однако вы можете переопределить на любой другой. Достаточно передать в качестве второго параметра или строку с названием сериализатора (сейчас поддерживаем: json, php и pickle) или сразу экземпляр класса Zend_Serializer с необходимыми вам опциями.

Примеры:

Partcl:
$push = new pushBridge_IO( new pushBridge_Adapter_Partcl(Array('secretKey' => 'Your secret key')) );
Pusher:
$push = new pushBridge_IO( new pushBridge_Adapter_Pusher(Array('appId' => 'Your app Id', 'authKey' => 'Your key', 'secretKey' => 'Your secret key', 'debug' => true)) );
Pubnub:
$push = new pushBridge_IO( new pushBridge_Adapter_Pubnub(Array('readKey' => 'Your subscribe key', 'authKey' => 'Your publish key', 'secretKey' => ' Your secret key')) );
BeaconPush:
$push = new pushBridge_IO( new pushBridge_Adapter_Beaconpush(Array('authKey' => 'Your API Key', 'secretKey' => 'Your secret key')) );
А во пример с кастомным сериализатором:
$push = new pushBridge_IO( new pushBridge_Adapter_Partcl(Array('secretKey' => ' Your secret key')), ‘php’ );
или
$push = new pushBridge_IO( new pushBridge_Adapter_Partcl(Array('secretKey' => 'Your secret key')), Zend_Serializer::factory('Wddx', Array(‘comment’ => ‘Powered by ZF+pushBridge.IO’) );

2. Просто отправляем сообщение.

На самом деле все не так и просто. Сервисы очень по разному работают с сообщениями. Самое простое - это модель канал->сообщение (или тег -> сообщение), другие же вносят новый уровень - канал -> событие -> сообщение. При этом только partcl очень толерантно относиться к содержимому сообщения, для него это просто строка, а ее понимание перекладываться полностью на клиента. Другие требуют специального оборачивания сообщений в json-структуры. Ни один из сервисов не поддерживает массовую отправку сообщений, когда в одном запросе можно было бы публиковать данные в разные каналы или несколько разных сообщений и событий в один канал. Этот момент мы уже учли и в следующей версии нашего сервиса Partcl.com такая возможность будет, параллельно в библиотеке появиться и эмуляция этого функционала для всех остальных (справедливости ради стоит сказать, что такой функционал реализован в классе для BeaconPush, но не портирован в мою библиотеку).

Общий метод отправки имеет такой вид:

$push->send( 'сообщение', 'channel', 'config' );

В метод адаптера попадает уже преобразованная строка, так что он гарантированно получает данные и они нечто большее, чем пустая строка. Однако, если сервис этого требует, адаптер сформирует поверх данных сообщения необходимую Json-структуру.

Если в сервисе используются каналы, то второй параметр это строка (или массив) каналов, куда следует отправить сообщение. если каналов нет, это ид тега или другой ид.

При необходимости задать код события следует использовать конфиг - третий параметр всегда массив. Событие задается в параметре event. Дополнительно можно отключить сериализацию, передав параметр serialize = false. Если сервис принимает, можно указать и дополнительные параметры сообщения, например флаг сохранения данных для X-Stream.ly или включить/отключить дебаг-режим для сообщения в Pusher-е.

Пример отправки простого сообщения:

Partcl:
$push->send('Hello world from pushBridge.IO', 'Your tag id', Array('serialize' => false));
Pusher:
$push->send('Hello world from pushBridge.IO', 'test_channel', Array('serialize' => false, 'event' => 'push_test', ‘debug’ => true));
Pubnub:
$push->send('Hello world from pushBridge.IO', 'my_channel');
x-Stream.ly:
$push->send('Hello world from pushBridge.IO', 'mychannel', Array('event' => 'my_event', ‘persisted’ => true));

Конечно, по дефолту, максимум параметров выставлено так, чтобы не было необходимости что-то задавать, часто даже имя канала или евента выставлено так, как они присваиваются сервисом после регистрации. Но для более полного понимания опций, следует обращаться к документации по конкретному сервису.

Исходный код библиотеки и краткие примеры:https://github.com/aleksraiden/pushBridge.IO/tree/ServiceWrapper

В планах на ближайший релиз: исправление ошибок, унификация метода send, поддержка публикации сразу нескольких переменный, реализация метода чтения данных, а также поднятие сайта проекта на домене pushcloudapi.com. Буду благодарен за замечания и дополнения.

  • Артем Таранов

    Здравствуйте. Спасибо большое за освещение данной темы.
    Вопрос: возможно ли как-нибудь средствами API (желательно сервиса Beaconpush) получать сообщения? Например, мне требуется написать приложение, где пользователи обмениваются сообщениями через МОЙ сервер, но доставка туда-сюда должна быть мгновенной.

  • Артем Таранов

    Здравствуйте. Спасибо большое за освещение данной темы.
    Вопрос: возможно ли как-нибудь средствами API (желательно сервиса Beaconpush) получать сообщения? Например, мне требуется написать приложение, где пользователи обмениваются сообщениями через МОЙ сервер, но доставка туда-сюда должна быть мгновенной.

    • Аноним

      Да, это можно почти на каждом сервисе. Они публикуют же сообщения через ваш сервер, а сторонний лишь доставляет сообщение уже обработанное вашим сервисом.

      • Артем Таранов

        Вот непонятно именно то, как получить на моем сервере сообщения от сервиса. Нет метода типа getMessage()

        • Александр Лозовюк

          Так вы посылайте их через свой сервис. Ведь не напрямую же с веб-страницы вы будете публиковать сразу в пуш-сервис. Для некотрых сервисов из статьи есть методы получения в серверном API сообщений, для тех, где нет, придеться эмулировать клиент.

          • Артем Таранов

            $push->send(‘Message’,’channel’); — отправили.
            А как принимать? До меня это не доходит. Я представляю примерно так: 
            $push->read(‘channel’);  
            Но такого ни где не видел… И не видел, чтобы был какой-либо метод типа: api/read/{channel}
            Не facepalm’ете  сильно,  с Comet серверами никогда не работал.

          • Александр Лозовюк

            Просто запишите в свою базу ДО вызова сенд.

            У Pubnub есть в их родном API субскрайб как у клиента, так и получение истории сообщений. 

            Например: https://github.com/pubnub/pubnub-api/blob/master/php/examples/subscribe-example.php

          • Артем Таранов

            Просто мне нужно именно на моем сервере получать сообщения. Это связано с использованием разных платформ(php , java[android] , и т.д.).
            Поэтому и хочется сделать свой API(backend) , который все клиенты будут дергать.
            За PubNub спасибо 🙂

          • Александр Лозовюк

            см. мой предыдущий ответ. в примере все есть

          • Артем Таранов

            Да-да, я сразу увидел. Спасибо большое!

  • Хороший вопрос размострен в посте. Большое спасибо мне очень помогло.

Developers.org.ua