Потоки обмена
Статья описывает, какими путями данные движутся через платформу при основных сценариях обмена: отправка сообщения, получение, подтверждение, повторная обработка, файловый обмен. Поток описывается на уровне «какой компонент с каким взаимодействует и в каком порядке», без углубления в код. Конкретные API-эндпоинты — в Транспортном API; внутреннее устройство — в Архитектуре сервиса и Работе с очередями.
Базовые участники
В каждом потоке участвуют (но не все одновременно):
- ИС-отправитель — клиентская система, шлющая сообщение.
- ИС-получатель — клиентская система, забирающая сообщение.
- nginx — единая точка входа извне, TLS-терминация.
- backend (Exchange-контур) — Сервер DataHUB, обрабатывающий обменные запросы.
- PostgreSQL — конфигурация обмена (системы, пакеты, маршруты, права).
- RabbitMQ — очереди сообщений.
- S3-хранилище — для бинарных вложений (см. Файловое хранилище).
Поток 1. Отправка сообщения
Базовый, самый частый поток: ИС-отправитель публикует сообщение в шину.
sequenceDiagram
participant S as ИС-отправитель
participant N as nginx
participant B as backend
participant C as Caffeine
participant P as PostgreSQL
participant R as RabbitMQ
S->>N: ① POST /api/.../send · Bearer token
N->>B: TLS terminate
B->>B: ② JWT-валидация
B->>C: ③ маршруты из кеша
alt кеш miss
C->>P: SELECT маршруты
P-->>C: маршруты
end
B->>R: ④ публикация копий в очереди получателей
B-->>N: ⑤ 200 OK + accepted id
N-->>S: 200 OK + accepted id
Ключевые моменты:
- Шаг ②. Валидация JWT отправителя — синхронная, не зависит от состояния других компонентов. Подробнее — Безопасность.
- Шаг ③. Маршруты читаются из кеша (Caffeine) с TTL и инвалидацией. Holdup на PostgreSQL — только если в кеше нет.
- Шаг ④. Для каждого подписанного получателя — отдельная копия в его очереди. Если подписчиков N, в RabbitMQ кладётся N копий с одинаковым
id. Это реализация модели «очередь на получателя» из Очередей сообщений. - Шаг ⑤. Ответ отправителю — после успешной публикации в RMQ. Если шаг ④ упал — отправитель получит ошибку и должен повторить.
- В БД шины при отправке ничего не пишется — централизованного «лога доставки сообщений» нет. Видимость прохождения реализована на стороне клиентского модуля (для 1С — отдельная страница статусов). Централизованный трекинг на стороне шины — в roadmap.
Поток 2. Получение сообщения
ИС-получатель забирает сообщения из своей очереди.
sequenceDiagram
participant R as ИС-получатель
participant N as nginx
participant B as backend
participant MQ as RabbitMQ
R->>N: ① GET /api/.../receive · Bearer token
N->>B: TLS terminate
B->>B: ② JWT-валидация
B->>MQ: ③ read из очереди получателя
MQ-->>B: сообщения
Note over MQ: ④ сообщения in-flight — не удалены, ждут ACK
B-->>N: ⑤ 200 OK + список сообщений
N-->>R: список сообщений
Ключевые моменты:
- Шаг ③. Backend читает сообщения из той RabbitMQ-очереди, которая закреплена за этим получателем. Имена очередей в RMQ детерминированы — см. Работа с очередями.
- Шаг ④. Сообщение остаётся в очереди в состоянии «in-flight» до явного подтверждения. Если получатель упадёт до подтверждения — сообщение вернётся в очередь и будет выдано заново.
- Получатель может работать в режиме short-polling (опрос с интервалом) или long-polling (соединение держится открытым, пока сообщение не появится).
Поток 3. Подтверждение приёма
После сохранения сообщения в свой локальный пул входящих получатель подтверждает приём — и только тогда сообщение окончательно удаляется из очереди шины.
sequenceDiagram
participant R as ИС-получатель
participant N as nginx
participant B as backend
participant MQ as RabbitMQ
Note over R: сохранила сообщение в локальный пул,<br/>закоммитила транзакцию
R->>N: ① POST /api/.../ack · {messageId}
N->>B: TLS terminate
B->>MQ: ② basic.ack
MQ-->>MQ: удалить сообщение из очереди
B-->>N: ③ 200 OK
N-->>R: 200 OK
Важно: ACK отдаётся не после применения сообщения к бизнес-данным, а после сохранения его в локальный пул входящих модуля получателя. Прикладная обработка происходит отдельно, отложенно, из этого пула — шина в неё уже не вовлечена. Подробнее о двухфазной модели — в Концепции / Жизненный цикл сообщения и в Архитектуре модулей интеграции.
После basic.ack сообщение окончательно удаляется из очереди RabbitMQ. В БД шины никакой записи об этом не появляется — централизованного лога доставки нет (см. пометку в Потоке 1).
Ключевые моменты:
- Шаг ①. Подтверждение отправляется после коммита транзакции сохранения сообщения в локальный пул входящих — это инвариант модели at-least-once. Прикладная обработка ещё не выполнена и шину не интересует.
- Если ① не пришёл (получатель упал, сеть оборвалась до отправки ACK) — сообщение в RabbitMQ остаётся в состоянии in-flight; через таймаут брокера возвращается в очередь; будет выдано заново (см. поток 5).
- Если шаг ② упал между «отдали ACK получателю» и «сделали basic.ack в RMQ» — сообщение тоже вернётся, и получатель увидит его второй раз. Защита — идемпотентность вставки в локальный пул (уникальный индекс по
idсообщения), см. Идемпотентность. - Если ошибка возникнет позже, при прикладной обработке из пула — шина уже ни при чём, сообщение из очереди удалено. Ретраи обработки идут внутри модуля по его настройкам.
Поток 4. Веерная рассылка
Один отправитель → N получателей. Поток отправки тот же, что и в Потоке 1, но шаг ④ кладёт N физических копий сообщения — по одной в каждую очередь подписчиков.
flowchart LR
S[ИС-отправитель] --> B[backend]
B --> MQ[RabbitMQ]
MQ --> A["очередь получателя A (id = 42)"]
MQ --> Bb["очередь получателя B (id = 42)"]
MQ --> C["очередь получателя C (id = 42)"]
Каждая копия живёт независимо: получатель A может уже подтвердить, B — ещё в очереди, C — в in-flight у медленного консьюмера. У всех трёх — одинаковый id (идентификатор исходного сообщения), что нужно для дедупликации на стороне получателей и для согласования действий между ними, если бизнес-логика этого требует.
Поток 5. Повторная доставка после сбоя
Сценарий: сообщение было выдано, ACK не пришёл.
sequenceDiagram
participant MQ as RabbitMQ
participant R as ИС-получатель
Note over MQ: ⏱ timeout in-flight
MQ->>MQ: возврат в исходную очередь
MQ->>R: следующий receive — то же сообщение (тот же id)
Note over R: идемпотентный обработчик распознаёт дубликат
R->>MQ: ACK
Никакой отдельной retry-очереди или DLQ в типовой схеме не используется — повтор это та же самая запись в той же самой очереди. См. Концепции / Жизненный цикл сообщения.
Поток 6. Файловый обмен
Файл передаётся отдельно от сообщения, через S3-хранилище. В payload сообщения — только ссылка.
sequenceDiagram
participant S as ИС-отправитель
participant S3 as S3-хранилище
participant B as backend
participant MQ as RabbitMQ
participant R as ИС-получатель
S->>S3: ① загрузка файла
S3-->>S: URL + метаданные (размер, md5)
S->>B: ② POST /send · ссылка и метаданные в payload
B->>MQ: публикация (Поток 1)
MQ->>R: ③ сообщение со ссылкой
R->>S3: ④ скачивание файла по ссылке
S3-->>R: файл
Шина не участвует в шагах ① и ④ — она только перевозит ссылку. Файл может быть выдан получателю много раз (например, при повторных обработках или просмотрах), не дублируясь в трафике шины. Подробнее — Файловое хранилище и Концепции / Файловый обмен.
Поток 7. Обмен с офлайн-получателем
sequenceDiagram
participant S as ИС-отправитель
participant B as backend
participant MQ as RabbitMQ
participant R as "ИС-получатель X"
S->>B: сообщение
B->>MQ: публикация в очередь X
Note over MQ,R: получатель офлайн — сообщения накапливаются ⏳
R->>B: вернулась онлайн, receive
B->>MQ: read пачками
MQ-->>B: накопленные сообщения
B-->>R: сообщения пачками
R->>B: ACK (Поток 3)
Note over MQ: очередь рассасывается до нуля
Для отправителя и backend поток не отличается от обычного — это нормальный режим работы.
Что дальше
- Работа с очередями — детали топологии RabbitMQ.
- Архитектура сервиса — внутренние компоненты backend, через которые проходят описанные потоки.
- Безопасность — как авторизация встроена в каждый из потоков.