Добрый день, уважаемые читатели. Совсем недавно я закончил разработку 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]/,'\\')+'","badge":"'+badge+'","sound":"default"}}'
.gsub(/['"\x0]/,'\\')
— это аналог 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