Модель обработки сообщений в STOMP.js естественным образом опирается на последовательную трансформацию входящих и исходящих данных через набор независимых обработчиков. Такой подход позволяет выстраивать расширяемую систему промежуточных слоёв, где каждый компонент отвечает за строго ограниченную задачу: модификацию заголовков, фильтрацию сообщений, логирование, повторные попытки, нормализацию payload или интеграцию с внешними состояниями приложения.
Цепочка обработчиков формирует линейный или частично разветвлённый pipeline, через который проходят все операции взаимодействия с брокером сообщений. Внутри STOMP.js это особенно важно из-за асинхронной природы доставки сообщений и необходимости гибко управлять жизненным циклом подписок.
Цепочка обработчиков представляет собой последовательность функций, каждая из которых получает результат предыдущей и передаёт управление дальше.
Формально:
input → handler1 → handler2 → handler3 → output
Каждый обработчик имеет единый контракт:
Контекст обычно включает:
STOMP.js работает поверх WebSocket и реализует протокол STOMP, где каждое сообщение может быть отправлено или получено через брокер (RabbitMQ, ActiveMQ, Apollo и др.).
Цепочка обработчиков используется в нескольких ключевых местах:
Такой дизайн позволяет отделить транспортный слой от бизнес-логики.
Типичный обработчик в цепочке имеет следующую форму:
function handler(context, next) {
// обработка контекста
return next(context);
}
Где:
context — объект сообщения или операцииnext — функция передачи управления дальше по
цепочкеКлючевой момент заключается в том, что обработчик может:
next, тем самым прерывая цепочкуSTOMP.js должен учитывать, что обработка сообщений часто включает асинхронные операции: декодирование, запросы к состоянию, запись в store, валидацию токенов.
handler1 → handler2 → handler3
Используется при простых трансформациях:
await handler1 → await handler2 → await handler3
Используется при:
Основной механизм построения цепочки — композиция функций.
function compose(handlers) {
return function(context) {
let index = -1;
function dispatch(i, ctx) {
if (i <= index) {
throw new Error("next() called multiple times");
}
index = i;
const handler = handlers[i];
if (!handler) return ctx;
return handler(ctx, (nextCtx) => dispatch(i + 1, nextCtx));
}
return dispatch(0, context);
};
}
Такая реализация обеспечивает:
nextВажным элементом цепочки является объект контекста. В STOMP.js он играет роль переносчика состояния между обработчиками.
Типичный контекст:
{
frame: {
command: "MESSAGE",
headers: {},
body: ""
},
subscription: {},
connection: {},
meta: {
timestamp: Date.now(),
retries: 0
}
}
Особенность заключается в том, что контекст не копируется, а передаётся по ссылке. Это позволяет:
Одной из ключевых возможностей является возможность прервать выполнение цепочки.
function authHandler(ctx, next) {
if (!ctx.frame.headers.authorization) {
ctx.error = "Unauthorized";
return ctx;
}
return next(ctx);
}
Разрыв используется в сценариях:
Порядок регистрации обработчиков напрямую влияет на результат выполнения.
Нарушение порядка приводит к:
При получении STOMP frame поток обработки обычно выглядит следующим образом:
Пример логической цепочки:
decode → parseFrame → normalize → middlewareChain → dispatchToSubscriber
Каждый этап может быть расширен через собственные обработчики.
При отправке сообщений цепочка используется аналогично:
validate → enrichHeaders → serialize → sendFrame
Обработчики могут:
Некоторые обработчики требуют задержки выполнения:
В таких случаях цепочка превращается в поток с отложенным разрешением:
async function tokenHandler(ctx, next) {
ctx.token = await refreshTokenIfNeeded();
return next(ctx);
}
STOMP.js позволяет строить вложенные pipeline, где один обработчик содержит собственную цепочку.
mainChain
↓
subChain (subscription-specific)
↓
handler
Это используется для:
Ошибки могут возникать на любом этапе. Их обработка требует отдельного слоя:
function errorHandler(ctx, next) {
try {
return next(ctx);
} catch (err) {
ctx.error = err;
return ctx;
}
}
Типичные стратегии:
Обработчики часто оборачиваются декораторами:
Пример:
function withLogging(handler) {
return async (ctx, next) => {
const start = Date.now();
const result = await handler(ctx, next);
const end = Date.now();
ctx.meta.duration = end - start;
return result;
};
}
Цепочка обработчиков часто интегрируется с реактивными хранилищами состояния:
В этом случае обработчики не только трансформируют сообщения, но и инициируют изменения состояния приложения.
При увеличении количества обработчиков возникают проблемы производительности:
Оптимизации включают:
Внутри STOMP.js цепочка обработчиков формирует слой абстракции между протоколом и прикладной логикой, обеспечивая управляемую последовательную обработку событий, предсказуемость потока данных и возможность гибкого расширения функциональности без изменения ядра транспортного механизма.