STOMP.js реализует транзакционный механизм поверх протокола STOMP 1.2, где ключевую роль играет идентификатор транзакции, связывающий несколько кадров (frames) в единую атомарную операцию. Этот идентификатор не является глобальным, не синхронизируется между клиентами и сервером как отдельная сущность, а существует исключительно в рамках одного соединения и управляется клиентской библиотекой и брокером сообщений.
В STOMP.js транзакция создаётся через вызов метода
client.begin(). В ответ возвращается объект транзакции,
содержащий поле id, которое и выступает идентификатором
транзакции.
Этот идентификатор:
Примерно внутренняя форма идентификатора представляет собой строку,
часто с префиксом или случайной частью, например tx-0,
tx-1 или UUID-подобное значение. Конкретный формат не
стандартизирован и зависит от реализации клиента.
STOMP работает через обмен текстовыми или бинарными кадрами. В рамках
транзакции каждый кадр должен быть помечен заголовком
transaction, значение которого равно идентификатору
активной транзакции.
Основные типы кадров, поддерживающие транзакционный контекст:
SENDACKNACKВо всех этих случаях идентификатор транзакции выполняет функцию связующего ключа, позволяя брокеру отложить окончательную обработку до получения команды завершения транзакции.
Пример логической структуры отправки сообщения:
SEND
destination:/queue/orders
transaction:tx-1
content-type:application/json
{"orderId":123}
До момента коммита сообщение не считается окончательно обработанным.
Идентификатор транзакции превращает набор независимых операций в
единый контекст исполнения. Все действия, выполненные с одинаковым
transaction id, попадают в одну логическую группу.
В рамках одной транзакции возможны:
Брокер не выполняет эти действия немедленно, а откладывает их до
получения COMMIT.
Ключевой принцип: идентификатор транзакции — это механизм группировки, а не блокировки.
Идентификатор существует ровно столько, сколько живёт транзакция:
Создание транзакции через BEGIN
Присвоение идентификатора (tx.id)
Использование идентификатора в кадровых операциях
Завершение через:
COMMITABORTУничтожение контекста транзакции на клиенте и сервере
После завершения транзакции идентификатор считается недействительным. Повторное использование того же значения в рамках того же соединения может привести к неопределённому поведению брокера или ошибке протокола.
При вызове:
const tx = client.begin();
STOMP.js отправляет на брокер кадр:
BEGIN
transaction:tx-1
С этого момента брокер фиксирует начало транзакционного контекста и начинает ассоциировать последующие кадры с данным идентификатором.
Важно, что идентификатор закрепляется за транзакцией на стороне
клиента сразу, а на стороне сервера — после обработки
BEGIN.
Каждое сообщение, отправленное в рамках транзакции, должно содержать заголовок:
transaction: <tx.id>
STOMP.js автоматически добавляет этот заголовок при использовании:
tx.send({ destination: "/queue/orders" }, body);
Внутри это эквивалентно:
client.send({
destination: "/queue/orders",
transaction: tx.id
}, body);
Брокер в этом случае не публикует сообщение сразу в очередь или подписчикам. Оно помещается в промежуточный транзакционный буфер.
При подписке с подтверждением (ack: client или
ack: client-individual) идентификатор транзакции также
используется для группировки подтверждений.
ACK в транзакции:
ACK
id:message-123
transaction:tx-2
NACK:
NACK
id:message-123
transaction:tx-2
Брокер откладывает окончательное подтверждение доставки до
COMMIT. Это позволяет атомарно подтвердить или отклонить
набор сообщений.
Команда завершения транзакции:
COMMIT
transaction:tx-3
После получения COMMIT брокер:
Идентификатор здесь служит ключом доступа к накопленным операциям. Без него брокер не может определить, какой набор действий требуется завершить.
При отмене транзакции:
ABORT
transaction:tx-4
Брокер:
Идентификатор используется для полного удаления связанного контекста.
После ABORT все операции, помеченные данным tx.id,
считаются несуществующими.
Идентификатор транзакции в STOMP.js имеет строго ограниченную область действия:
Это означает, что два клиента могут иметь одинаковый
tx-1, но брокер различает их по соединению.
STOMP.js допускает создание нескольких параллельных транзакций:
const tx1 = client.begin();
const tx2 = client.begin();
Каждая из них получает собственный идентификатор:
Обе транзакции могут выполняться независимо, без пересечения
состояний. Брокер хранит отдельные буферы для каждого
transaction id.
Нарушения использования идентификатора приводят к различным ошибкам протокола или поведения брокера:
transaction id в SEND →
сообщение игнорируется или отклоняетсяSTOMP.js не всегда явно валидирует такие ошибки на клиенте, поэтому контроль корректности идентификаторов ложится на уровень логики приложения.
Технически возможно повторное использование значения
tx.id после завершения транзакции, однако это считается
нежелательной практикой.
Причины:
Рекомендуется всегда создавать новую транзакцию вместо переиспользования идентификатора.
Вся транзакционная модель STOMP.js строится вокруг идеи, что идентификатор транзакции:
Фактически transaction id выступает временным маркером
состояния, который существует только до момента фиксации или отката.
Разные брокеры (ActiveMQ, RabbitMQ STOMP plugin, Apollo, Artemis) используют идентификатор транзакции как внутренний ключ:
Несмотря на единый стандарт STOMP, внутренняя реализация обработки
transaction id может отличаться, особенно в части
оптимизации хранения и времени жизни контекста.
Идентификатор позволяет гарантировать согласованность операций:
Это достигается исключительно за счёт того, что все кадры помечены
одинаковым transaction id, а брокер откладывает их
применение до финального сигнала завершения.
Таким образом, идентификатор транзакции является центральным элементом механизма атомарности в STOMP.js, связывая клиентские операции в единый управляемый поток исполнения.