Допустим, мы разрабатываем веб-сервис, который обрабатывает входящие 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
Автор::