-
Notifications
You must be signed in to change notification settings - Fork 2
Работа с Middleware
← Пользовательские транспорты | Слушатели и обработчики →
Middleware (посредники) в Messenger – это компоненты, которые обрабатывают сообщения до и/или после основных действий, образуя цепочку вызовов (pipeline) внутри шины сообщений. Посредники могут использоваться для выполнения кросс-срезных задач: логирование, валидация, авторизация, метрика и т.д., применяемых к каждому сообщению.
Каждая шина сообщений имеет свою цепочку middleware. По умолчанию Messenger автоматически добавляет три ключевых middleware в конец цепочки каждой шины (в указанном порядке):
-
AddBusNameMiddleware: помечает сообщение маркой с именем шины. Эта middleware добавляет к сообщению
BusNameStamp
с именем текущей шины, если такой марки еще нет. Это помогает отслеживать, через какую шину прошло сообщение. -
SendMessageMiddleware: отвечает за отправку сообщения в соответствующий транспорт. Эта middleware проверяет, не содержит ли сообщение марку
ReceivedStamp
(означающую, что сообщение пришло из транспорта). Если сообщение новое (локально Dispatch-нутое),SendMessageMiddleware
определяет по маршрутизации, в какие транспорты его нужно отправить (их может быть несколько), генерирует событиеSendMessageToTransportsEvent
(которое могут слушать ваши слушатели), отправляет сообщение в каждый указанный транспорт через методSend
и добавляет маркуSentStamp
для каждого успешного отправления. После этого управление передается следующему звену цепочки. Если сообщение уже имеетReceivedStamp
(т.е. мы получили его от брокера), эта middleware ничего не отправляет и сразу вызывает следующий обработчик, позволяя перейти к локальной обработке сообщения. -
HandleMessageMiddleware: осуществляет вызов пользовательских обработчиков. Эта middleware проверяет, не содержит ли сообщение марку
SentStamp
(признак того, что оно уже отправлено внешнему транспортом). Если да – значит, это сообщение предназначалось для отправки наружу и локально обрабатывать его не надо (middleware вернет успешно, завершая цепочку на этом). Если же маркерыSentStamp
нет (например, сообщение пришло из брокера или используетсяin-memory
транспорт), то middleware находит все зарегистрированные обработчики для типа данного сообщения и вызывает каждый по очереди. Ошибичное поведение: если ни одного обработчика не найдено, выдается предупреждение и возвращается ошибка. Каждый обработчик вызывается и может вернуть либоerror
, либо (если метод возвращает два значения)результат и error
. Messenger игнорирует возвращаемый результат с точки зрения бизнес-логики, но сохраняет его: после успешного выполнения обработчика добавляется маркаHandledStamp
с информацией о том, какой обработчик выполнился и чему равен результат (если был). Если любой обработчик вернул ошибку, цепочка прерывается: ошибка логируется, иHandleMessageMiddleware
возвращает ошибку, не вызывая последующие обработчики. Таким образом, при множественных обработчиках на сообщение, они выполняются последовательно в порядке регистрации, и сбой в одном предотвратит выполнение следующих.
Вы можете добавлять собственные middleware в цепочку для расширения логики обработки. Для этого:
-
Реализуйте middleware. Интерфейс
api.Middleware
предполагает методHandle(ctx context.Context, env Envelope, next NextFunc) (Envelope, error)
. Ваш middleware код должен вызватьnext(ctx, env)
, чтобы передать управление дальше по цепочке (если вы не хотите прервать обработку). Вы можете модифицировать сообщение (например, добавитьstamp
) до или после вызова следующего обработчика, либо вообще решить не вызывать next (например, для фильтрации/блокировки сообщения). -
Пример: создадим простейший логирующий middleware, который пишет в лог информацию о сообщении:
type LoggerMiddleware struct{}
func (m *LoggerMiddleware) Handle(ctx context.Context, env api.Envelope, next api.NextFunc) (api.Envelope, error) {
fmt.Printf("[LoggerMiddleware] processing message of type %T\n", env.Message())
// вызвать следующий middleware/обработчик
envOut, err := next(ctx, env)
if err != nil {
fmt.Printf("[LoggerMiddleware] error handling message: %v\n", err)
} else {
fmt.Printf("[LoggerMiddleware] finished processing message of type %T\n", env.Message())
}
return envOut, err
}
Этот middleware выводит сообщение до и после обработки остальными компонентами цепочки.
-
Зарегистрируйте middleware. Чтобы Messenger узнал о вашем middleware, нужно зарегистрировать его в билдере перед сборкой. Для этого используется метод
RegisterMiddleware(name string, mw Middleware)
. Например:
b := builder.NewBuilder(cfg, log)
b.RegisterMiddleware("logger", &LoggerMiddleware{})
Здесь logger
– это имя, под которым middleware будет доступен. Это имя вы используете в конфигурации YAML для привязки к конкретной шине (см. выше секцию buses). В нашем примере, если в конфиге у шины default
указано middleware: ["logger"], то при сборке Build()
билдер найдет зарегистрированный LoggerMiddleware по имени и включит его в цепочку для шины.
-
Порядок выполнения middleware. Middleware из конфигурации выполняются до встроенных (
AddBusName
,SendMessage
,HandleMessage
). Порядок заданный в YAML сохраняется при регистрации. Как только все ваши middleware отработают, управление перейдет кAddBusName
→SendMessage
→HandleMessage
. Помните, чтоAddBusName
всегда идет первым из встроенных, поэтому ваш первый middleware в списке будет выполняться после добавления BusNameStamp. В большинстве случаев порядок не критичен, но важно понимать, например, что логгер как выше будет логировать событие до фактической отправки/обработки сообщения, а второй вывод – после полной обработки (включая доставку и выполнение хендлеров). -
Условное применение middleware. Если вам нужно, чтобы middleware применялся только к некоторым сообщениям, можно внутри
Handle
проверить тип или содержимоеenv.Message()
и решить, вызывать лиnext
или вернуть результат немедленно. Также, если надо прекратить дальнейшую обработку (например, фильтр), вы можете не вызыватьnext
и просто вернутьnil
,nil (или nil, err для обозначения ошибки)
. Вернувnil
,nil без вызова next
, вы предотвратите обработку сообщения остальными middleware и хендлерами – фактически "проглотив" сообщение.
Messenger не предоставляет готовых middleware кроме встроенных системных, поэтому разработчики свободны реализовывать необходимые им посредники. Приведенная выше LoggerMiddleware
– типичный шаблон. Другие примеры: middleware валидации данных (возвращает ошибку, если сообщение не проходит проверку), middleware дедубликации, middleware измерения времени выполнения и пр. Зарегистрировав их и указав в конфигурации, вы можете гибко настроить поведение каждой шины под ваши требования.