Skip to content

Работа с Middleware

Gerfey edited this page Jul 24, 2025 · 2 revisions

Пользовательские транспорты | Слушатели и обработчики


Middleware (посредники) в Messenger – это компоненты, которые обрабатывают сообщения до и/или после основных действий, образуя цепочку вызовов (pipeline) внутри шины сообщений. Посредники могут использоваться для выполнения кросс-срезных задач: логирование, валидация, авторизация, метрика и т.д., применяемых к каждому сообщению.

Каждая шина сообщений имеет свою цепочку middleware. По умолчанию Messenger автоматически добавляет три ключевых middleware в конец цепочки каждой шины (в указанном порядке):

  1. AddBusNameMiddleware: помечает сообщение маркой с именем шины. Эта middleware добавляет к сообщению BusNameStamp с именем текущей шины, если такой марки еще нет. Это помогает отслеживать, через какую шину прошло сообщение.

  2. SendMessageMiddleware: отвечает за отправку сообщения в соответствующий транспорт. Эта middleware проверяет, не содержит ли сообщение марку ReceivedStamp (означающую, что сообщение пришло из транспорта). Если сообщение новое (локально Dispatch-нутое), SendMessageMiddleware определяет по маршрутизации, в какие транспорты его нужно отправить (их может быть несколько), генерирует событие SendMessageToTransportsEvent (которое могут слушать ваши слушатели), отправляет сообщение в каждый указанный транспорт через метод Send и добавляет марку SentStamp для каждого успешного отправления. После этого управление передается следующему звену цепочки. Если сообщение уже имеет ReceivedStamp (т.е. мы получили его от брокера), эта middleware ничего не отправляет и сразу вызывает следующий обработчик, позволяя перейти к локальной обработке сообщения.

  3. 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 отработают, управление перейдет к AddBusNameSendMessageHandleMessage. Помните, что AddBusName всегда идет первым из встроенных, поэтому ваш первый middleware в списке будет выполняться после добавления BusNameStamp. В большинстве случаев порядок не критичен, но важно понимать, например, что логгер как выше будет логировать событие до фактической отправки/обработки сообщения, а второй вывод – после полной обработки (включая доставку и выполнение хендлеров).

  • Условное применение middleware. Если вам нужно, чтобы middleware применялся только к некоторым сообщениям, можно внутри Handle проверить тип или содержимое env.Message() и решить, вызывать ли next или вернуть результат немедленно. Также, если надо прекратить дальнейшую обработку (например, фильтр), вы можете не вызывать next и просто вернуть nil, nil (или nil, err для обозначения ошибки). Вернув nil, nil без вызова next, вы предотвратите обработку сообщения остальными middleware и хендлерами – фактически "проглотив" сообщение.

Messenger не предоставляет готовых middleware кроме встроенных системных, поэтому разработчики свободны реализовывать необходимые им посредники. Приведенная выше LoggerMiddleware – типичный шаблон. Другие примеры: middleware валидации данных (возвращает ошибку, если сообщение не проходит проверку), middleware дедубликации, middleware измерения времени выполнения и пр. Зарегистрировав их и указав в конфигурации, вы можете гибко настроить поведение каждой шины под ваши требования.


Пользовательские транспорты | Слушатели и обработчики

Clone this wiki locally