Пользователь
0,0
рейтинг
7 ноября 2012 в 02:07

Разработка → Особенности работы с Apple push notification service

Добрый день, уважаемые хабражители. Совсем недавно я закончил разработку apns-сервиса и хотел бы поделиться некоторыми особенностями работы с ним. Статья не является пошаговой инструкцией, а описывает трудности и подводные камни с которыми может столкнуться разработчик. Примеры кода буду приводить на Ruby, но все написанное актуально и для других платформ, в частности, PHP.


Об общих принципах работы с apns написано много статей, да и официальная документация достаточно понятна и прозрачна, поэтому перейдем сразу к делу:

Особенность номер раз — JSON

Именно в этот формат необходимо преобразовать сообщение для отправки на сервер apple, но здесь нас ждет разочарование. Казалось бы, проще всего сгенерировать сообщение с помощью встроенных функций преобразования массивов в json вот так:
result = {}
result['aps'] = {}
result['aps']['alert'] = m.message
result['aps']['badge'] = 0
result['aps']['sound'] = 'default'
json = result.to_json

Но не все так просто. Вы вдруг обнаружите, что максимальная длина текста сообщения составляет всего 40-50 символов. Почему так? Дело в том, что функция to_json в Ruby преобразует все не ASCII символы в последовательность /uXXXX, которая при упаковке сообщения занимает 4! байта. Для обхода этого ограничения необходимо формировать сообщение в обычном текстовом формате. Например, вот так:
json = '{"aps":{"alert":"'+message.gsub(/['"\\\x0]/,'\\\\\0')+'","badge":"'+badge+'","sound":"default"}}'

.gsub(/['"\\\x0]/,'\\\\\0')
— это аналог PHP-функции addslashes(). Если ее не применить, то сообщения со знаками препинания и некоторыми служебными символами не дойдут пользователю и будут сочтены apple как ошибочные.

Особенность номер два — обрыв соединения и обратная связь

Если при отправке очередного сообщения произошла ошибка, то apple просто разрывает связь, не дожидаясь окончания отправки всех сообщений в очереди. Но есть возможность узнать, какая именно ошибка произошла при отправке. Для этого необходимо составить сообщение особым образом и присвоить каждому уникальный номер:
mes =  [1, #Уникальный ID сообщения#, #Максимальное время ожидания доставки#, 0, 32, token, 0, payload.bytesize, payload].pack("cNNcca*cca*")

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

Особенность номер три — обратная связь

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

Особенность номер четыре — массовая рассылка сообщений

Соединение с серверами apple устанавливается с помощью сертификата, выданного определенному приложению. Именно так Apple определяет, к какому приложению на устройстве пользователя относится отправленное уведомление. Следовательно, для отправки сообщений нескольким приложениям (а именно такая задача стояла у сервиса, разработанного мной), необходимо для каждого массива сообщений создавать отдельное соединение. Частая ошибка, совершаемая разработчиками — они пытаются отправлять сообщения небольшими партиями по 100-200 штук, каждый раз создавая и разрывая соединение. Это может быть воспринята сервером как попытка атаки и ваш сервис будет заблокирован. Чтобы избежать этого, нужно отправлять все сообщения через одно соединение, даже если их несколько десятков тысяч. И отсюда вытекает особенность номер шесть.

Особенность номер шесть — время наш враг

При большом количестве сообщений время исполнения скрипта отправки может быть достаточно велико и на большинстве серверов выполнение будет завершено по таймауту в 30 секунд. Решение — разделение логики создания и отправки сообщений. Я сделал это с помощью демона (в ROR за это отвечает gem daemons. Через веб интерфейс в базу добавляется текст сообщения и фильтр, по которому при отправке будут выбираться токены из базы, а демон каждые 10 секунд опрашивает базу на наличие новых заданий и отправляет сообщения. В такой реализации есть еще один неоспоримый плюс — мы можем настроить отложенную отправку сообщений.

Заключение

Не смотря на все подводные камни и тонкости, которые нужно держать в голове, реализация сервиса push сообщений у apple сделана на высшем уровне. Сервис быстр, удобен и требует минимум ресурсов для отправки огромного количества сообщения. Для примера: Чтобы отправить 15000 сообщений, мы должны передать на сервер всего около 3,5-4 мегабайт данных
Алексей @lex33
карма
5,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (9)

  • 0
    Учитывая, что сервер разрывает соединение при отправке ошибочного токена, отправлять 10тыс.+ сообщений как-то странно.
    • 0
      Сервер разрывает соединение не при отправке ошибочного токена, а при ошибке в формате сообщения. И, по опыту, это не такой частый случай. В случае ошибки можно сохранить последнее отправленное сообщение и после исправления продолжить с того же места. А почему нужно отправлять сразу много я написал в статье. Мне пришлось работать с базой 50k+ токенов и если отправлять небольшими порциями, можно ставить крест на этом сервисе.
  • +3
    «Особенность номер 2» называется Enhanced binary interface.
    «Особенность номер 3» называется Feedback service
    «Особенность номер 7» подводных камней там еще больше… *мрачно*
  • 0
    • 0
      Спасибо, но перед началом работы я изучил все существующие гемы. К сожалению, ни один не подошел. Кстати, тот что вы советуете реализован не лучшим образом и в нем есть как минимум 2 ошибки, о которых я писал.
  • +1
    Я достаточно активно работаю со всеми пуш провайдерами: apple, google, win, blackberry, даже, прости господи, nokia.
    Из всех них, общение с apns дается сложнее всего. По сравнению с другими очень много неудобств:
    — Нельзя узнать статус конкретного сообщения.
    — 255 байт на все сообщение — это же прошлый век. Я хочу передавать больше данных в приложение
    — Разрывы соединений просто разрывают мозг. Нельзя так делать. Это генерирует следующий пункт
    — Нельзя после завершения отправки просто взять и закрыть соединение. Нужно еще сколько-то времени подождать. Вдруг APNS сам закроет соединение и нужно будет отправить что-то повторно?

    У всех провайдеров есть какие-то ограничения и недостатки. Например у win и nokia нет возможности отправиьт пачку сообщений одним запросом. На каждый девайс отдельно. Blackberry любит говорить просто, что запрос принят на обработку. Статус сообщения получить можно, но геморно и за деньги, но это детали.

    А ваш сервис куда-то выложен в паблик? Потыкаться можно?
    • 0
      Не могу не согласиться со всем написанным. Нет, сервиса в паблике нет, т.к. он разрабатывался для внутреннего пользования клиента. Если есть конкретные вопросы — с радостью отвечу или поделюсь кусочком кода.
      • 0
        Нет, спасибо, своего кода достаточно с этим делом =)
  • 0
    Я с пушами рабоатаю уже не один год (Под проектом AppRus). в результате, могу добавить:

    1. 50к+ — Отправлять используя AMQP
    2. Разрыв соединения — Да, есть, здесь уж ничего не поделаешь, но кто мешает наново заново открыть соединение?
    3. Относительно длины 255 байт. Да, с этим они загнали, но вполне это можно решить передачей какого-то «ключа», а уже приложение при обработке пуша сделает то, что нужно.
    4. Feedback — здесь очень сильно нужно переделить внимание. Согласно правилам Аппле, Вы обязательно должны опрашивать сервер на наличие «invalid devices», чтобы потом не слать пуши на эти токены. Если этот пункт будет проигнорирован, то аппле имеет полное право заблокировать приложение.

    В общем, проблем и сильных подводных камней здесь вообще нету.
    P.S. (Пиарюсь :) ):
    Можете воспользоваться пакетом github.com/ZhukV/AppleApnPush для отправки пушей (Этот пакет уже проверен веременем + поддерживает отправку со списков: AMQP, Redis)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.