Пуш уведомления являются замечательным механизмом, доступным для всех разработчиков мобильных (и не только) приложений. При помощи таких уведомлений можно рассказывать пользователям о наступлении важных событий, давая возможность максимально оперативно отреагировать, даже когда приложение не запущено. Кроме того, подобный подход позволяет привлечь дополнительное внимание к вашему продукту. В этой статье мы расскажем какие особенности мы обнаружили в процессе реализации подобного рода функциональности в крупных проектах, а также предоставим набор рекомендаций.
Прежде всего стоит отметить, что современные сервисы работают на множестве платформ, поэтому сразу стоит закладываться на использование:
- Apple Push Notifications Service — APNS
- Google Cloud Messaging — GCM
- HTML5 WebSocket и Comet для реализации уведомлений в браузере
Принимая решение об использовании APNS и GCM стоит учитывать принципиальные технические особенности их использования: отсутствие гарантий доставки сообщений, отсутствие удобных систем логирования и статистики отправленных и полученных сообщений, ограничения на размер сообщений.
Опыт показывает, что несмотря на отсутствие гарантий, указанные сервисы весьма стабильны, обладают высокой производительностью и доставляют сообщения практически моментально (за несколько секунд). Поэтому в большинстве случаев, использование APNS и GCM — это именно то, что вам нужно. Если же речь идет о сообщениях, которые должны доставляться со 100% вероятностью и с гарантированной скоростью доставки, то скорее всего стоит озаботиться построением своей инфраструктуры доставки сообщений.
При поступлении пуш уведомления на клиентское устройство приложение адресат может быть запущено (открыто или в фоновом режиме), а может и не быть. Обработка сообщения в этом случае различается. Если приложение запущено, то обработкой занимается само приложение (срабатывает callback метод). Если приложение не запущено, то уведомление обрабатывается операционной системой (устанавливается название уведомления, проставляется счетчик), а при запуске приложения накопленные уведомления передаются на обработку.
Процесс интеграции с сервисами доставки сообщений довольно простой и хорошо описан: документация по APNS и документация по GCM. Далее мы расскажем про основные наблюдения и дадим некоторые рекомендации.
- Сообщения можно рассылать несколькими потоками с нескольких серверов — это очень полезно когда нужно разом разослать множество уведомлений
- Часто проблемы с производительностью связаны со скоростью подготовки сообщений (выборка адресатов, формирование сообщения и т.п.) — т.е. находятся полностью на вашей стороне и не связаны с производительностью сервисов Apple и Google
- Иногда пользовательские токены становятся невалидными, например, из-за того, что пользователь удалил ваше приложение. Чтобы удалять из вашей базы такие токены, вы должны периодически обращаться к feedback-сервису, на который будет поступать информация о таких токенах. В том случае, если вы этого не делаете, ваши уведомления могут начать обрабатываться медленнее или вообще не доходить до адресатов.
- Вы всегда можете задать вопрос в форуме Apple — они весьма открыты и проверяют проблемы с доставкой по своим логам (достаточно знать токен устройства и время отправки уведомления)
- Существует возможность рассылать оповещения на тестовые версии приложений, не загруженные в App Store.
- Для тестовой (sandbox) среды требуется сгенерировать отдельные сертификаты. В теории токены пользователей также могут отличаться для каждого приложения, сертификата и среды. Не путайте сертификаты рассылки токенов и не смешивайте между собой токены из разных приложений и разных сред.
- Обычно оповещения отправляются в асинхронном режиме, не дожидаясь ответа от сервера APNS. В случае ошибки в сообщении, сервер просто разрывает соединение. Это сделано для того, чтобы максимально ускорить доставку сообщений. Но в том, случае, когда вам необходимо понять причину ошибки, может быть полезным особый формат сообщения с включенным флагом “error response”. В этом случае каждое сообщение можно пометить уникальным идентификатором и получить код ошибки, соответствующий этому сообщению.
Процесс доставки уведомлений на сервере
Если предполагается что сервис будет рассылать достаточно большое количество уведомлений на разные устройства (iOS, Android) а также в браузеры, так или иначе возникает вопрос возможности распараллеливания процесса рассылки. Из опыта можно выделить три этапа подготовки и рассылки уведомлений каждый из которых параллелится лучше или хуже в зависимости от логики сервиса.
- Выборка пользователей, которые должны получить очередное уведомление. Из соображений производительности на этом этапе лучше всего выбирать пользователей, которые должны получить уведомление независимо от платформы на которую уведомление должно быть доставлено. Хорошо если есть возможность держать информацию об этих пользователей в кэше, а не выполнять выборку из базы данных для каждого уведомления, но это не всегда возможно. Этап выборки пользователей параллелится достаточно сложно. Как вариант можно предложить, работать в разных процессах с разными фрагментами массива пользователей, ограниченными основным идентификатором пользователя.
- Подготовка уведомлений в форматах конкретных систем доставки, на основе выборки пользователей, полученной в предыдущем пункте. Данный этап хорошо параллелится, потому что массивы пользователей могут обрабатываться независимо.
- Взаимодействие с внешним сервисом доставки уведомлений. Данный этап также хорошо параллелится, так как все сервисы push-уведомлений позволяют принимать уведомления для одного приложения в несколько потоков даже с одного IP адреса. Если же организовать рассылку с нескольких серверов или с одного в несколько потоков то можно достичь очень высокой скорости взаимодействия с внешним сервисом. Практика работы с APNS показывает, что для каждого приложения внутри APNS также существует очередь, поэтому предел скорости “закачки” уведомлений в систему APNS достигается в районе 10 — 15 потоков рассылки. Далее, увеличение количества потоков “закачки” не приводит к увеличению скорости доставки уведомлений на устройства.
Обзор готовых решений для работы с push-уведомлениями
Необходимо отметить, что любое из готовых решений, о которых пойдет речь, упрощает лишь взаимодействие с внешним сервисом. Из этого следует, что процесс внеднения push-уведомлений в любой проект достаточно трудоемкий, потому что первый и второй этап работы с уведомлениями всегда придется реализовывать самостоятельно.
Любое достаточно серьезное решение работы с уведомлениями должно обертывать сам сервис, избавляя разработчика от необходимости разбираться в тонкостях взаимодействия с ним, а также, что более важно, предоставлять возможности построения распределенной рассылки. Исходя из этого, если некое абстрактное решение, не отвечает этим двум критериям, то его всерьез рассматривать не стоит.
На данный момент, есть достаточно много готовых сервисов для рассылки уведомления на iOS устройства, и практически нет решений для сервиса GCM. Однако, эта проблема компенсируется тем, что работы с GCM гораздо проще для разработчика, потому что само взаимодействие с сервисом сводится к обычным POST запросам в случае с GCM, в то время как сервис APNS основан на перманентном сокете.
Наиболее интересным решением для PHP на данный момент является библиотека apns-php. Библиотека предоставляет возможности работы с уведомленими в объектно-ориентированном стиле, а также содержит шаблон для многопоточного рассылающего демона. Для организации очереди уведомлений при многопоточной рассылке испольуется механизм shared memory.
Для python есть pyapns. В отличие от apns-php, pyapns является сервисом, который запускается как сервер под управлением twisted и все взаимодействие с ним ведется по сети. Предполагается, что множество рассылающих сервисов находится за прокси-балансировщиком, что позволяет строить горизонтально масштабируемую систему рассылки. В pyapns уже включены клиенты для работы с ним из python и ruby. Учитывая работу pyapns как отдельного сервиса, возможно написание клиента для любого языка, в том числе PHP.
К сожалению, нельзя сказать что для сервиса GCM доступно такое же количества готовых решений для рассылки. В основном встречаются примитивные обертки для разных языков, которые до некоторой степени скрывают работы с сервисом. Однако, как уже говорилось выше, сам процесс взаимодействия с GCM проще чем с APNS, поэтому написание собственной параллельной рассылки не должно составить большого труда.
Для разработки push-уведомлений под web всегда есть два сценария: либо пользоваться сторонним сервисом ( http://www.webdistortion.com/2012/02/02/real-time-push/ ), либо полностью реализовывать все самостоятельно. При самостоятельной реализации основными компонентами системы будут:
- Сокет-сервер. Для реализации хорошо подходит Node.js, однако если существует практика написания сокет-сервера на основном языке проекта (например на python), то лучше использовать эту возможность, и не добавлять в стек дополнительные технологии.
- Очередь сообщений pubsub (publish subscribe). Для этой задачи хорошо подходит Redis.
- Клиентский модуль для работы с websocket на Javascript.
Желаем большого количества адресатов пуш уведомлений вашим сервисам! 🙂