Собственный Push&Pull клиент

Выберите инструмент для разработки с AI-агентом:

  • используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
  • используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации

Собственный клиент держит соединение с серверами Push&Pull напрямую: приложение меняет свое состояние и обновляет интерфейс сразу, не опрашивая REST.

Обмен устроен так. Серверная часть приложения публикует событие методом pull.application.event.add, событие попадает в канал приложения на серверах Push&Pull, а клиент читает канал по открытому соединению. Клиент подключается сразу к обоим каналам приложения. Определения терминов раздела и сроки жизни канала и конфигурации собраны в статье Интерактивность в приложениях: обзор методов.

Собственный клиент нужен, когда приложение работает отдельно от интерфейса Битрикс24: серверный процесс интеграции, десктопное приложение, веб-приложение на своем домене вне фрейма. Например, обработка на вашем сервере разбирает выгрузку несколько минут и по завершении сообщает об этом клиенту, а тот сразу показывает результат, не опрашивая сервер.

В других случаях подойдут решения из соседних статей:

  • приложение открыто в Битрикс24, и достаточно штатного клиента — Push&Pull в браузере
  • события нужны, когда страница приложения закрыта, а Битрикс24 открыт в браузере — PAGE_BACKGROUND_WORKER
  • уведомить нужно пользователя вне интерфейса Битрикс24 — pull.application.push.add

Клиент работает только в контексте приложения. Конфигурацию подключения он запрашивает методом pull.application.config.get, которому нужен OAuth-токен и scope pull, а вебхук такой контекст не создает.

Что нужно перед началом

  • установленное приложение со scope pull
  • серверная часть, которая умеет вызывать REST с OAuth-токеном приложения

Конфигурацию подключения запрашивает серверная часть приложения — OAuth-токен остается на ней. Клиенту передайте только адрес сервера и параметры подключения из ответа.

CHANNEL_ID, jwt и clientId — это доступ к каналу. Тот, кто их получил, читает события приложения до конца срока жизни канала. Не выводите их в логи, не записывайте в открытые хранилища и не передавайте третьим сторонам.

Как подключиться к серверу

Сервер поддерживает два способа подключения: websocket и long polling. По умолчанию подключайтесь по websocket. Long polling нужен только для устройств без поддержки websocket или когда соединение регулярно срывается.

  1. Получите параметры подключения методом pull.application.config.get
  2. Проверьте server.server_enabled. Значение false означает, что Push&Pull в этом Битрикс24 не настроен: подключаться не к чему, и переподключение не поможет
  3. Возьмите из объекта server защищенный адрес нужного типа — websocket_secure или long_pooling_secure. Параметры подключения передаются в строке запроса, поэтому незащищенные websocket и long_polling берите только там, где защищенного адреса нет. Если server.websocket_enabled равен false, websocket выключен — подключайтесь по long polling
  4. Выберите, чем авторизовать подключение. Поля jwt и clientId вместе не приходят, поэтому веток всего две:
    • в ответе есть поле jwt — передайте его в GET-параметре token. Каналы уже зашиты в токен, CHANNEL_ID не нужен. Токен выдает только сервер версии 5 и выше, поэтому в облачном Битрикс24 эта ветка не встречается
    • поля jwt нет — соберите GET-параметр CHANNEL_ID из значений channels.private.id и channels.shared.id, именно в таком порядке и через /
  5. Если в ответе пришло поле clientId, добавьте его к адресу отдельным параметром

Значение CHANNEL_ID можно подставить как есть или закодировать вместе со всей строкой запроса — сервер принимает оба варианта. При кодировании разделитель / превращается в %2F и остается разделителем.

Идентификаторы каналов не разбирайте на части: у личного канала значение id само содержит двоеточие, и это не разделитель каналов.

Публичные идентификаторы public_id и объект publicChannels для подключения не используются ни в одной из веток. Состав объекта server описан в разделе Объект server, поля канала — в разделе Объект канала shared и private, а условия, при которых приходят jwt и clientId, — в разделе Объект result.

Пример подключения по websocket к общему серверу Push&Pull:

wss://rtc-cloud-ms1.bitrix24.tech/subws/?CHANNEL_ID=beb502091dfc9b93d7fd648aa4ec332e%3A7cc478c89de71ec78bf4820d3d814a3e.4f5466742ca1e59e263fee732a7dbe002889ba91%2F1ab4f7a440cea35a1abccd5c2566c688.b33914ef342e5cd21e4fbcf4ac92acd2e9ea3755&clientId=fcda45d0859442735f07b8bb5825ded1&format=json

Пример подключения по websocket к собственному серверу Push&Pull. Адрес и путь задает администратор при настройке сервера, поэтому они отличаются от адресов общего сервера:

wss://rt.**put.your-domain-here**/sub/?CHANNEL_ID=46a437d2336d4a88e4e9b3cd956ecf45:6221e0eb48981fce67cf4756e82e8102.7910bb25e660bf211fdec15e33c5e25e4c3b644a/fb9f7e13dc3d595c5aefe1a0216c27a2.2887eebc6ae160713a732893462dce9d8e23a7b0

В первом примере адрес записан с процентным кодированием. Параметра format во втором примере нет: на собственном сервере версия по умолчанию — 2.

Пример подключения с токеном, когда в ответе метода пришло поле jwt:

wss://rt.**put.your-domain-here**/sub/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.**put_jwt_here**&format=json

В адресе подключения может быть до пяти GET-параметров:

  • CHANNEL_ID или token — авторизация подключения, обязательно одно из двух
  • clientId — только на общем сервере Push&Pull
  • format — формат команд
  • mid либо пара tag и time — чтение истории канала

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

Запрашивайте конфигурацию заново, когда подходит к концу срок end одного из каналов или время exp самой конфигурации. В коробочной версии exp приходит не всегда — если поля нет, опирайтесь на end каналов и на команды config_expire и server_restart.

Как ведет себя открытое соединение

Чтобы читать события приложения, отправлять в сокет после подключения ничего не нужно: обмен односторонний, команды идут от сервера к клиенту.

Признак живого соединения — сообщения от сервера: кроме событий он регулярно шлет служебные. Штатный клиент Битрикс24 считает соединение зависшим, если не получил ни одного сообщения за 20 секунд, — закрывает сокет и переподключается. Держите в своем клиенте такое же правило по времени: долгую тишину считайте обрывом, а не затишьем.

Подключение по long polling

Long polling работает с теми же GET-параметрами, что и websocket, — меняется только базовый адрес, его берут из server по правилу шага 3. Имя поля long_pooling_secure приходит из API с опечаткой, pooling вместо polling; читайте его как есть.

Клиент держит один открытый GET-запрос и переоткрывает его после каждого ответа:

  1. Отправьте GET-запрос на собранный адрес
  2. Получили 200 — разберите тело ответа как команды и сразу отправьте следующий запрос
  3. Получили 304 — команд не было, сразу отправьте следующий запрос
  4. Получили любой другой статус — считайте это ошибкой подключения и выдержите задержку из раздела Работа с ошибками
  5. Ответа нет дольше 60 секунд — оборвите запрос сами и откройте новый

Штатный клиент Битрикс24 ждет ответа 60 секунд и не открывает следующий запрос, пока не завершился предыдущий. Держите то же правило — иначе в канал уйдет очередь параллельных запросов.

Общий формат команд от сервера

Читать события приложения можно в текстовом формате или в JSON — этого достаточно. Штатный клиент Битрикс24 умеет еще два режима, JSON-RPC и бинарный, но они нужны ему для служебного обмена, а не для чтения канала.

Формат зависит от версии сервера — она приходит в поле server.version ответа метода pull.application.config.get. На общем сервере Push&Pull версия всегда равна 4.

Для сервера версии 4 и выше добавьте при подключении GET-параметр &format=json — команды придут в формате JSON:

[
    {"id":320146,"mid":"14526134350000000000320146","channel":"6221e0eb48981fce67cf4756e82e8102","tag":"672","time":"Thu, 29 Jun 2017 09:50:16 GMT","text":{},"extra":{}},
    {"id":320147,"mid":"14526134350000000000320147","channel":"6221e0eb48981fce67cf4756e82e8102","tag":"673","time":"Thu, 29 Jun 2017 09:50:17 GMT","text":{},"extra":{}}
]

Для сервера версии 3 и ниже команды приходят текстом вида:

#!NGINXNMS!#{"id":320146,"mid":"14526134350000000000320146","channel":"6221e0eb48981fce67cf4756e82e8102","tag":"672","time":"Thu, 29 Jun 2017 09:50:16 GMT","text":{},"extra":{}}#!NGINXNME!#
#!NGINXNMS!#{"id":320147,"mid":"14526134350000000000320147","channel":"6221e0eb48981fce67cf4756e82e8102","tag":"673","time":"Thu, 29 Jun 2017 09:50:17 GMT","text":{},"extra":{}}#!NGINXNME!#

Чтобы разобрать такую команду, возьмите текст между маркерами #!NGINXNMS!# и #!NGINXNME!# и преобразуйте его в JSON.

В канал приходят все команды, включая управляющие команды модуля pull, поэтому фильтруйте их сами по полям text.module_id и text.command.

Сама команда в обоих форматах имеет единый вид:

{
    "id": 320146,
    "mid": "14526134350000000000320146",
    "channel": "6221e0eb48981fce67cf4756e82e8102",
    "tag": "672",
    "time": "Mon, 03 Oct 2017 06:36:01 GMT",
    "text": {
        "module_id": "application",
        "command": "test_event",
        "params": {
            "grid_id": 15,
            "status": "done"
        }
    },
    "extra": {
        "server_time": "2017-10-03T08:36:01+02:00",
        "server_time_unix": 1507012561,
        "server_time_ago": 0,
        "server_name": "rt1.bitrix24.com",
        "revision_web": 19,
        "revision_mobile": 3,
        "channel": "6221e0eb48981fce67cf4756e82e8102"
    }
}

где:

  • id — идентификатор сообщения
  • mid — идентификатор сообщения для восстановления истории, только для версии сервера 3 и выше
  • channel — идентификатор канала, только для версии сервера 3 и выше. Для версии сервера 1 идентификатор приходит в extra.channel, а для версии 2 не приходит ни там, ни там — берите его из значения, которое сами подставили в CHANNEL_ID
  • tag — E-tag для восстановления истории, для версии сервера 2 и ниже
  • time — время сообщения для восстановления истории, для версии сервера 2 и ниже
  • text — структура, описывающая действие команды:
    • module_id — идентификатор модуля, отправившего команду. Для событий приложения это значение параметра MODULE_ID метода pull.application.event.add, по умолчанию application
    • command — идентификатор команды
    • params — дополнительные данные для выполнения команды
  • extra — структура с дополнительными сведениями:
    • server_time — время сервера на момент формирования команды в формате ATOM
    • server_time_unix — время сервера на момент формирования команды в формате Unix timestamp с долями секунды
    • server_time_ago — количество секунд, прошедших с момента отправки команды. Сервер это поле не присылает, его подставляет клиент, посчитав по server_time_unix
    • server_name — имя сервера, отправившего команду
    • revision_web — ревизия протокола Push&Pull для браузерного клиента
    • revision_mobile — ревизия протокола Push&Pull для мобильного клиента
    • channel — идентификатор канала для версии сервера 1, смотрите описание поля channel выше

Как проверить подключение

Отправьте событие из серверной части методом pull.application.event.add с COMMAND, равным test_event. В канал должна прийти команда, у которой text.module_id равен application, а text.command — test_event.

Если команда не пришла, проверьте по порядку:

  • соединение установлено и не закрылось сразу после открытия
  • CHANNEL_ID собран по правилу из раздела Как подключиться к серверу и не потерял части при кодировании адреса
  • срок каналов не истек — сравните текущее время с end из ответа pull.application.config.get
  • событие отправлено с тем же USER_ID, чей канал читает клиент, либо без USER_ID — тогда оно уходит в общий канал

Работа с ошибками

Обрабатывать ошибки подключения обязательно: сервер заблокирует за подозрительную активность клиент, который переподключается без задержки. Конкретные интервалы ниже — ориентир, а не требование протокола.

Если подключение к серверу приводит к ошибкам, увеличивайте задержку перед следующей попыткой. Штатный клиент Битрикс24 выдерживает такие задержки:

  • обрыв уже установленного соединения — 0,5 секунды
  • после первой и второй неудачной попытки — 5 секунд
  • после третьей и четвертой — 25 секунд
  • с пятой по девятую — 45 секунд
  • начиная с десятой — 60 секунд

Добавляйте к задержке случайную надбавку от 0 до 20%, чтобы клиенты не переподключались одновременно. Счетчик неудачных попыток сбрасывайте, как только соединение установилось.

Если websocket не поднимается несколько попыток подряд и соединение закрывается с кодом 1006 или 1008, на компьютере пользователя, скорее всего, заблокирован протокол websocket. В этом случае предусмотрите запасное подключение по long polling.

Полностью переходить на long polling стоит, только если по websocket не удалось подключиться ни разу. Если раньше соединение поднималось, переключайтесь временно и пробуйте websocket снова — штатный клиент возвращается к нему через 30 минут.

Управляющие команды сервера

Предусмотрите в клиенте обработку управляющих команд. Ниже показано содержимое поля text — сами команды приходят в том же конверте, что и события приложения.

channel_expire

Сервер сообщает, что срок работы канала истекает.

{
    "module_id": "pull",
    "command": "channel_expire",
    "params": {
        "action": "reconnect",
        "channel": {
            "id": "46a437d2336d4a88e4e9b3cd956ecf45.7910bb25e660bf211fdec15e33c5e25e4c3b644a",
            "type": "shared"
        },
        "new_channel": {
            "id": "fb9f7e13dc3d595c5aefe1a0216c27a2.2887eebc6ae160713a732893462dce9d8e23a7b0",
            "start": "2017-06-28T09:57:48+02:00",
            "end": "2017-06-28T21:57:48+02:00",
            "type": "shared"
        }
    }
}

Параметры команды

Название
тип

Описание

action
string

Действие, которое должен выполнить клиент:

  • reconnect — переподключиться к новому каналу из new_channel
  • get_config — запросить конфигурацию заново

channel
object

Информация о канале, для которого получена команда

new_channel
object

Информация о новом канале. Приходит, только если action равен reconnect

Как обработать команду

Когда придет команда channel_expire, выполните шаги в зависимости от значения action:

  • action равен reconnect
    • замените информацию о текущем канале данными из new_channel
    • переустановите подключение к серверу
  • action равен get_config
    • отключитесь от сервера
    • запросите новые данные о каналах методом pull.application.config.get
    • установите подключение к серверу заново

config_expire и server_restart

Сервер сообщает, что его настройки изменились.

{
    "module_id": "pull",
    "command": "config_expire",
    "params": {}
}

Команда server_restart приходит в том же виде, отличается только значение command:

{
    "module_id": "pull",
    "command": "server_restart",
    "params": {}
}

Как обработать команды config_expire и server_restart

Если поступила команда config_expire или server_restart:

  • отключитесь от сервера
  • через случайный промежуток от 10 до 120 секунд запросите новые данные о каналах методом pull.application.config.get. Разброс нужен, чтобы после перезапуска сервера клиенты не пришли за конфигурацией одновременно
  • установите подключение к серверу заново

Продолжите изучение