Стейблкоины

Переход на виртуальные потоки с использованием Micronaut Loom Carrier: эволюция многопоточности в Java

Стейблкоины
Transitioning to Virtual Threads Using the Micronaut Loom Carrier

Подробное руководство по переходу на виртуальные потоки с помощью экспериментального режима Loom Carrier в Micronaut HTTP Server на базе Netty. Обзор архитектурных особенностей, преимуществ, проблем и оптимизаций нового подхода к асинхронному программированию и взаимодействию с IO.

Современная индустрия разработки программного обеспечения постоянно стремится к повышению производительности приложений при одновременном упрощении архитектур. На протяжении многих лет классическая модель многопоточности в Java сталкивалась с проблемами — большими затратами памяти на системные потоки, сложностями масштабирования и дорогостоящими переключениями контекста. В этой связи переход на виртуальные потоки (virtual threads) становится настоящим прорывом, предоставляя разработчикам возможность работать с тысячами параллельных задач, при этом сохраняя привычную блокирующую модель программирования. Новая разработка в экосистеме Micronaut под названием Loom Carrier раскрывает потенциал виртуальных потоков в связке с высокопроизводительным сетевым фреймворком Netty, обеспечивая уникальный баланс между реактивной и блокирующей парадигмами. Micronaut HTTP Server Netty версии 4.

9 представляет экспериментальный режим «loom carrier mode», позволяющий выполнять виртуальные потоки прямо на event loop, сохраняя высокую производительность и низкие задержки. Несмотря на то, что виртуальные потоки легко интегрируются в большую часть существующих блокирующих API, традиционная модель Netty изначально базируется на асинхронном программировании с малым количеством потоков событий (event loops). Одна из целей разработки Loom Carrier — добиться удобства написания привычного кодa, но без издержек многопоточности, блокировок и сложной асинхронной логики. Рассмотрим подробнее архитектуру Netty и ее особенности. Netty изначально оптимизирован для работы с асинхронным вводом-выводом и минимальным числом рабочих потоков, обслуживающих множество соединений.

Это экономит ресурсы системы — вместо того чтобы создавать отдельный поток на каждое соединение, Netty использует ограниченный пул «событийных» потоков, эффективно управляя ресурсами и снижая нагрузку на ОС. При использовании нативных библиотек, таких как io_uring в Linux или OpenSSL для криптографии, достигается дополнительное ускорение обработки ввода-вывода. Тем не менее, асинхронная модель предъявляет высокие требования к качеству кода. Его необходимо писать так, чтобы не блокировать event loop, иначе вся система может остановиться. Код становится сложным в сопровождении и отладке, приходится использовать колбэки, реактивные стримы и прочие нехитрые конструкции.

 

Появление виртуальных потоков в Java меняет правила игры. Виртуальные потоки представляют собой легковесные летучие контексты выполнения с минимальным потреблением памяти и возможностью быстрого переключения. Они основаны на механизме продолжений (continuations), который при блокирующих операциях, таких как sleep или IO, позволяет JVM сохранить состояние потока и освободить носитель (carrier thread) для выполнения других задач. Это избавляет от необходимости переписывать существующий блокирующий код под асинхронный, сохраняя его простой и понятный стиль. Однако сочетание виртуальных потоков с Netty без доработок вызывает ряд сложностей.

 

В частности, в JDK виртуальные потоки по умолчанию запускаются на ForkJoinPool (FJP), у которого отсутствует контроль над потоками, что приводит к возможным проблемам с производительностью, связанным с распределением задач и необходимостью обеспечить, чтобы event loop оставался на одном и том же платформенном потоке — требование, критически важное для оптимизаций на базе io_uring. Ситуация усложняется использованием нативных блокирующих вызовов, которые не позволяют JVM приостанавливать виртуальные потоки, что ставит carrier thread в простое и снижает производительность. Решение, предложенное в проекте Micronaut с Loom Carrier, заключается в создании собственного executor для виртуальных потоков — специального carrier, связанного с event loop. В этом режиме для каждого event loop выделяется отдельный carrier thread, на котором запускается event loop, а виртуальные потоки создаются также на этом carrier, что позволяет избежать лишних переключений контекста между executor и event loop. Кроме того, carrier управляет процессом пробуждения event loop, если виртуальный поток ожидает выполнения после ввода-вывода, обеспечивая максимальную отзывчивость системы.

 

Такой подход позволяет обработать множество запросов, как на отдельных виртуальных потоках, так и группами, увеличивая пропускную способность без ухудшения задержек. Интересным улучшением является «immediate run» — возможность запускать обработчик запроса непосредственно внутри обработчика события чтения на event loop, что уменьшает задержки и дает значительный прирост производительности, особенно при обработке нескольких запросов в одном событии. Одной из важных оптимизаций является поддержка client affinity — механизма, при котором HTTP клиентские и database соединения разделяют event loop сервера. Это снижает количество переключений между потоками и синхронизационных издержек, существенным образом ускоряя взаимодействия. Ранее эта оптимизация была доступна только для асинхронного кода, но с Loom Carrier теперь virtual threads тоже могут воспользоваться преимуществами client affinity, что приближает их производительность к реактивному коду.

При работе с блокирующим вводом-выводом, не основанным на Netty, например, при работе с JDBC драйверами и connection pool'ами, виртуальные потоки активно используют два слоя pollers. Сначала пытается выполниться неблокирующее чтение, а при отсутствии данных поток регистрируется в суб-поллере, который сам представлен виртуальным потоком. При необходимости суб-поллер ждёт сигнала от мастер-поллера — уже платформенного потока, — что неизбежно приводит к контекстному переключению. Это накладывает ограничения на оптимизацию ввода-вывода в таких сценариях, и пока что в Micronaut Loom Carrier переход виртуального потока для выполнения IO туда-сюда между event loop и ForkJoinPool существенно не влияет на производительность, хоть и требует двух переключений. В тестах с интерактивной нагрузкой база данных демонстрирует производительность, сравнимую с FJP, но с чуть меньшей максимальной пропускной способностью, что связано с асинхронными операциями пула соединений.

Одним из существенных вызовов при данном подходе становится балансировка задач и миграция потоков между carrier threads. Стандартный ForkJoinPool свободно перераспределяет задачи между платформенными потоками, что хорошо с точки зрения загрузки процессоров, но приводит к проблемам с согласованностью данных и задержкам при объединении ответов. В Loom Carrier миграция задач более ограничена, что избавляет от накладных расходов на синхронизацию, но потенциально снижает параллелизм в нагрузках с резкими пиками. Таким образом, приложение иногда будет работать лучше на FJP, а иногда — на event loop carrier. Возможность переключаться между вариантами дает гибкость и позволяет найти оптимальный баланс для конкретных нужд.

Безопасность и стабильность работы в режиме Loom Carrier также заслуживают внимания. Использование виртуальных потоков в carrier тредах создает риск закономерных взаимоблокировок (deadlocks). Например, если виртуальный поток удерживает блокировку и приостанавливается, а носительский поток пытается захватить тот же лок — возникает бесконечное ожидание. Чтобы предотвратить такие сценарии, разработчики рекомендуют избегать общих блокировок между carrier и виртуальными потоками. В частности, активное логирование на event loop может спровоцировать такие состояния, если логгирование синхронизировано с обычными synchronized-блоками.

Конструктивное решение — перевести весь event loop на виртуальный поток, что позволяет приостанавливать event loop и давать возможность выполнять другие задачи без блокировок. Важным аспектом для оптимизации производительности является управление локальными кешами и пулом ресурсов. В классической реализации Netty использует ThreadLocal для хранения буферов, позволяя избежать дорогостоящей синхронизации. При переходе на виртуальные потоки возникает конкуренция за эти кеши, так как ThreadLocal становится менее эффективным. Некоторые улучшения, такие как использование arenas и привязка кешей к carrier threads, уже реализуются, но необходимо дальнейшее развитие API JDK для более надежного решения этой задачи.

В перспективе планируется предоставить JDK специальные API для управления ресурсами, которые сохранили бы преимущества многопоточности, но не мешали бы эволюции виртуальных потоков. При рассмотрении всей картины можно смело утверждать, что Loom Carrier — это новый шаг в эволюции работы с виртуальными потоками в Java, способный кардинально упростить разработку высокопроизводительных серверных приложений. Он позволяет сочетать удобство блокирующего кода с эффективностью реагирующей модели, обеспечивает значительную гибкость управления, и открывает путь к тому, чтобы виртуальные потоки стали стандартом для серверных решений на базе Micronaut и других JVM-фреймворков. Тем не менее, проект находится в активной стадии развития. Особое внимание уделяется устранению проблем с синхронизацией, оптимизацией работы с IO и балансировкой нагрузки между потоками.

Сообщество разработчиков приглашается к тестированию и обратной связи, что важно для формирования стабильного API и адаптации технологии к разнообразным бизнес-кейсам. В заключение, учитывая огромный потенциал виртуальных потоков и постепенное развитие поддержки в JVM, переход на Loom Carrier представляет собой оптимальный способ объединить лучшие практики асинхронного программирования с простотой и безопасностью классической модели. Подобные технологии призваны минимизировать трудоемкость поддержки кода, улучшить отзывчивость приложений и сократить затраты на эксплуатацию многопоточных серверов. Их внедрение в ближайшем будущем обещает стать одним из ключевых трендов в мире Java-разработки и микросервисных архитектур.

Автоматическая торговля на криптовалютных биржах

Далее
Crypto 101: Privacy and Security on Cryptocurrency Platforms
Пятница, 24 Октябрь 2025 Основы криптовалют: как обеспечить конфиденциальность и безопасность на платформах хранения и обмена

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

Cryptocurrency isn’t private—but with know-how, it could be
Пятница, 24 Октябрь 2025 Криптовалюта и приватность: как технологии помогут обеспечить безопасность и анонимность

Рассмотрены особенности приватности в криптовалютах, существующие риски и способы повышения безопасности при использовании блокчейна и цифровых валют на основе исследований экспертов по кибербезопасности.

Crypto Exchange Emphasizes Privacy by Offering Coin Conversions Without
Пятница, 24 Октябрь 2025 Как криптобиржа BitConvert меняет представление о приватности при обмене криптовалюты

BitConvert предлагает уникальный сервис обмена криптовалют без регистрации и сбора личных данных, обеспечивая максимальную анонимность и безопасность пользователей в современной цифровой экономике.

Crypto privacy is in greater jeopardy than ever before — here's why
Пятница, 24 Октябрь 2025 Почему приватность в криптовалюте находится под угрозой как никогда раньше

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

New Anti-Crypto Movement Escalates Congress’s Assault On Privacy - Forbes
Пятница, 24 Октябрь 2025 Новая Анти-Криптовалютная Волна: Усиление Нападок Конгресса на Финансовую Конфиденциальность

В современной политической повестке США все больше места занимает борьба с криптовалютами, что неизбежно ведет к усилению контроля за финансовой конфиденциальностью граждан. Обсуждаются законопроекты и инициативы, которые могут существенно ограничить анонимность в цифровых транзакциях и привести к масштабному вторжению государства в личные финансы.

 Bitcoin digests US PPI win with $120K liquidity grab on bulls' radar
Пятница, 24 Октябрь 2025 Биткоин на фоне снижения индекса PPI в США: новая ликвидность и перспектива роста до $120 000

На фоне неожиданного снижения индекса цен производителей (PPI) в США биткоин демонстрирует устойчивость и намечает потенциальный рост к отметке $120 000, что привлекает внимание как институциональных, так и розничных инвесторов.

EU Sanctions crypto entities for election interference, disinformation
Пятница, 24 Октябрь 2025 ЕС вводит санкции против крипто-структур за вмешательство в выборы и распространение дезинформации

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