В процессе разработки одного из сервисов я обнаружил, что при асинхронной обработке данных с почтового сервера терялась диагностическая информация, передаваемая через MDC.
Первоначально код выглядел как-то так:
Unis.voidItem()
.chain(() -> mailService.process(msg));
Возникающие проблемы:
- Потеря MDC: MDC хранится в ThreadLocal, а асинхронные операции могут переключаться между потоками. Из-за этого данные, установленные в MDC, терялись или оказывались перепутанными, что усложняло диагностику.
- Отсутствие изоляции: Поскольку вызов выполнялся в общем контексте, отсутствовало явное управление окружением выполнения. Это могло привести к состояниям гонки или другим непредсказуемым ошибкам.
- Блокировка event loop: Если метод
mailService.process
выполнял блокирующий код, это могло негативно сказаться на производительности.
Стоит подчеркнуть, что если бы запрос поступал через стандартные механизмы Quarkus (gRPC, GraphQL, Kafka), то управление контекстом осуществлялось бы системой, и подобных проблем бы не возникало. Однако в нашем случае вызов инициировался через org.apache.camel.Processor
, что требовало дополнительных усилий по сохранению контекстной информации.
Анализ показал, что основная проблема кроется в потере MDC при переключении между потоками. В условиях асинхронного выполнения вызовы, инициированные через Camel, не гарантируют автоматическую передачу контекста, что приводит к потере данных, необходимых для корректного логирования и диагностики.
Чтобы решить эту проблему, было решено явно создать дублированный контекст и переключить выполнение на него. Код был изменён следующим образом:
final Context duplicateContext = Context.newInstance(
VertxContext.getOrCreateDuplicatedContext(vertx.getDelegate())
);
Unis.voidItem()
.emitOn(duplicateContext::runOnContext)
.chain(() -> mailService.process(msg));
Преимущества данного подхода:
- Сохранение MDC: Дублированный контекст позволяет перенести все данные, связанные с текущим окружением (включая MDC), что обеспечивает корректное логирование даже при асинхронном выполнении.
- Изоляция выполнения: Переключение на новый контекст гарантирует, что все последующие операции выполняются в заданном окружении, исключая неожиданные переключения потоков.
- Устранение блокировки event loop: Если метод
mailService.process
содержит блокирующие или ресурсоёмкие операции, их выполнение в изолированном контексте помогает снизить негативное влияние на основной event loop.
Таким образом, если ваши асинхронные операции инициируются через нестандартные компоненты, такие как Apache Camel, и Quarkus не управляет контекстом напрямую, создание дублированного контекста становится необходимым для корректной работы.
Мета информация
Область:: 00 Quarkus
Родитель::
Источник::
Создана:: 2025-02-24
Автор::