Допустим, мы разрабатываем веб-сервис, который обрабатывает входящие HTTP-запросы. Каждому запросу назначается уникальный идентификатор, который должен сохраняться на протяжении всей обработки запроса, включая асинхронные операции, такие как:

  • обращение к базе данных,
  • вызовы внешних API,
  • запись логов,
  • отправка событий в Kafka или RabbitMQ.

В синхронных приложениях все просто: данные привязываются к потоку, который обслуживает запрос, и остаются неизменными на протяжении всего выполнения.

Но в асинхронных системах задача усложняется. В отличие от синхронных программ, где каждый запрос выполняется в одном потоке от начала до конца, в асинхронных системах выполнение может передаваться между разными потоками, а один поток может обслуживать множество операций. В итоге:

  • Потоки могут переключаться — одна часть кода выполняется в одном потоке, а затем продолжение запроса передается другому. Данные, связанные с первым потоком, могут потеряться.
  • Логирование может работать некорректно — если идентификатор запроса не передается автоматически, логи окажутся несвязанными, и отладка системы станет сложной.
  • Настройки или переменные запроса могут сбрасываться — например, если определенные переменные нужны для аутентификации, они могут «потеряться» между вызовами.

В таких условиях возникает важный вопрос: как сохранить состояние данных при переключении между потоками? Например, если у нас есть уникальный идентификатор запроса для логирования, как убедиться, что он не потеряется при передаче управления между разными частями кода?

Для решения этой проблемы в Quarkus и Vert.x используются контексты. Они позволяют хранить локальные данные и передавать их между асинхронными вызовами. Однако существует несколько видов контекстов, каждый из которых решает свои задачи. Разберем их подробнее.

В большинстве случаев Quarkus и Vert.x автоматически управляют контекстами, но если ваш код выполняется в разных потоках, стоит использовать дублированный контекст. Это поможет избежать неожиданных ошибок и сделать вашу систему более стабильной и предсказуемой.

В Quarkus и Vert.x есть несколько видов контекстов, каждый из которых выполняет свою роль.

Корневой контекст

Корневой контекст — это самый базовый контекст, который используется для работы с глобальными процессами, выходящими за пределы одного запроса.

Особенности корневого контекста:

  • Он общий для всех процессов, что делает его небезопасным для хранения локальных данных.
  • Если попытаться сохранить в нем данные, они могут быть случайно переданы в другие запросы.
  • Любая попытка модифицировать его может привести к исключению UnsupportedOperationException.

Не используйте

Не используйте корневой контекст для хранения уникальных данных запроса. Вместо этого лучше работать с обычным или дублированным контекстом.

Обычный (основной) контекст

Это контекст, который автоматически создается системой при обработке запроса. Он содержит:

  • информацию о текущем event loop,
  • локальные данные, такие как настройки, переменные и данные для логирования (например, MDC),
  • механизм автоматической передачи данных между асинхронными вызовами в пределах одного event loop.

Когда вы обрабатываете HTTP-запрос или сообщение из Kafka, Quarkus автоматически создает основной контекст и использует его для выполнения всей операции.

Однако проблема в том, что если выполнение запроса переключается между потоками, этот контекст может потеряться.

Дублированный контекст

Обычный контекст автоматически передается между асинхронными вызовами, но только в пределах одного event loop. Если выполнение задачи выходит за его пределы (например, передается другому потоку), контекст может не сохраниться.

Решение — создать дублированный контекст. Это копия текущего контекста, которая остается доступной даже при переключении потоков.


Мета информация

Область:: 00 Quarkus
Родитель::
Источник::
Создана:: 2025-02-24
Автор::

Дополнительные материалы

Дочерние заметки