![Что такое CRaC? Ускоряем запуск и прогрев Java Что такое CRaC? Ускоряем запуск и прогрев Java](/assets/images/2024-07-24-crac/logo.png)
Что такое CRaC? Ускоряем запуск и прогрев Java
Большинство приложений разрабатывается и разворачивается в облаке, где время выполнения кода стоит денег. Для компании, у которой сотня приложений, аренда облачных сервисов обойдётся в круглую сумму каждый месяц. Например, когда вы развёртываете сервис в Kubernetes или Deckhouse. Чтобы не переплачивать за медленно работающие приложения и сэкономить деньги компании, вы можете ускорить работу Java-приложения.
Java-приложение тратит много времени на запуск и прогрев JVM. Один из способов решения этой проблемы — использовать Coordinated Restore at Checkpoint (согласованное восстановление из контрольной точки, сокр. CRaC) — проект OpenJDK, в рамках которого разрабатывается новый API его реализация, для сохранения контрольной точки и восстановления состояния приложения.
В наших сборках Axiom JDK 17 и 21, а также в готовых Axiom Runtime Container вы уже можете попробовать Java с поддержкой CRaC.
Эта статья открывает цикл статей о CRaC и его использовании в Java-приложениях.
В этой статье:
- Что такое Coordinated Restore at Checkpoint (CRaC)?
- Каким приложениям нужен CRaC
- CRaC в сравнении с другими способами ускорения Java-приложения
- Поддержка CRaC в Spring Boot
- Что учесть при использовании CRaC
- Заключение
Что такое Coordinated Restore at Checkpoint (CRaC)?
Механизм работы CRaC похож на игровой прогресс в видеоиграх. Чтобы не начинать игру сначала каждый раз, когда вы её запускаете, игра сохраняет ваш прогресс по мере прохождения. Перед тем, как вы выйдете из игры, система сохранит текущий прогресс на диск. Как только вы вернётесь, игра начнётся с того же места, где вы остановились.
CRaC работает так же, только с JVM и Java-приложением.
Вы запускаете Java-приложение и в произвольный момент времени создаёте контрольную точку (англ. checkpoint), сохраняя состояние JVM и образ запущенного приложения — снапшот (англ. snapshot). Все открытые файлы и сетевые соединения закроются. После создания контрольной точки вы сможете восстановить состояние приложения и JVM. Вы также можете распространять снапшоты для развёртывания приложения на нескольких серверах.
Так, Java с поддержкой CRaC позволяет приостановить работу приложения и запустить его с того момента, когда оно было приостановлено. CRaC ускоряет запуск JVM после сбоя или прерывания работы.
Надёжность использования контрольных точек
С помощью API Coordinated Checkpoint/Restore можно дать приложению понять, что с него планируют снять снапшот или перезапустить его из состояния снапшота. Так, приложение может не создавать контрольную точку, если посчитает момент неподходящим для сохранения состояния. Например, при сохранении пользовательских данных. Кроме того, приложение закроет сетевые соединения и открытые дескрипторы файлов, а после восстановления вернётся к нормальной работе и отреагирует на возможные изменения в окружении, произошедшие с момента создания контрольной точки.
CRIU или CRaC
CRIU (Checkpoint and Restore in Userspace) — это технология для Linux, являющаяся основой для CRaC. CRIU использует интерфейс ядра ptrace и позволяет “замораживать” запущенное приложение и восстанавливать его из сохраненных файлов контрольных точек. Существующая реализация CRaC в OpenJDK включает в себя CRIU и несколько улучшений и корректировок для Java-приложений. Однако CRaC накладывает больше ограничений на процесс восстановления. Например, CRIU может сохранять состояние TCP-сокета, а затем восстанавливать соединение. В CRaC же все соединения должны быть закрыты до создания контрольной точки, что делает весь процесс более надёжным.
Каким приложениям нужен CRaC
CRaC больше всего подойдёт приложениям:
- с коротким временем работы (serverless-приложения),
- частыми перезапусками,
- небольшим количеством выделенных ресурсов CPU,
- развёртыванием на нескольких серверах.
Если выделить слишком мало памяти для серверов, приложение запускается медленно, что увеличивает расходы компании. Если размер выделенных ресурсов сервера слишком велик, то компания переплачивает за ресурсы, которые не используются. Кроме того, когда Java-приложение проходит через стандартные процессы запуска и JIT-компиляции, потребление процессора обычно выше, чем в нормальном состоянии.
С использованием CRaC приложения запускаются практически мгновенно и с максимальной производительностью. Это минимизирует задержки (latency) и оптимизирует потребление ресурсов.
CRaC в сравнении с другими способами ускорения Java-приложения
Существуют и другие способы решить проблемы медленного запуска и прогрева Java-приложения:
- Application Class Data Sharing (AppCDS). Помогает сократить время запуска, но не влияет на прогрев.
- Ahead-of-Time (AOT). Компиляция создаёт нативный образ с почти мгновенным запуском. Однако AOT может не подойти для проектов, использующих динамические возможности Java.
- Project Leyden. Всё ещё в процессе становления. Поскольку он опирается на концепцию статических образов, то его плюсы и минусы такие же, как и у AOT-компиляции.
CRaC же минимизирует время запуска и прогрева, а также сохраняет преимущества JIT-компиляции для дальнейшей оптимизации производительности.
Поддержка CRaC в Spring Boot
Spring Boot реализовал экспериментальную поддержку снапшотов с использованием технологии CRaC. В будущих релизах Spring Boot планируется встроенная поддержка CRaC. Благодаря поддержке CRaC в Axiom JDK вы можете достичь высокой скорости запуска и прогрева в проектах Spring Boot с минимальным переписыванием кода. Контейнеры Axiom с поддержкой CRaC не нужно дополнительно настраивать для работы с CRaC API. Это обеспечивает его бесшовное включение в рабочие нагрузки и уменьшает время запуска в 164 раза.
В некоторых случаях при использовании контейнеров Axiom с поддержкой CRaC размер конечных образов уменьшается на 10%.
Что учесть при использовании CRaC
CRaC представляет собой подход с сохранением состояния (англ. stateful), поскольку сохраняет точное состояние запущенного приложения в определённый момент времени вместе с информацией о куче Java, собственной памяти, JIT-компилированном коде, настройках и т. д. В результате чего снапшот может содержать конфиденциальные данные. Помните об этом при работе с данной технологией. У вас есть два пути:
Проанализировать, как создаются, хранятся и вызываются файлы снапшотов. Убедиться, что снапшот делается в тот момент, когда JVM не хранит никакой конфиденциальной информации.
Также учитывайте возможные проблемы с генерацией случайных чисел. Поскольку стартовое число (seed) java.util.Random создается при инициализации, псевдослучайные числа будут предсказуемы после восстановления. Один из способов уменьшить последствия этой проблемы — создать новое стартовое число (seed) в методе afterRestore(). Однако лучшим решением является использование java.security.SecureRandom, который обеспечивает более надёжную генерацию случайных чисел. С помощью SecureRandom вы можете удалить стартовое число SecureRandom (наряду с NativePRNG) и заблокировать случайные операции перед созданием снапшота. Блокировка снимается в методе afterRestore() и обеспечивает безопасную генерацию случайных чисел после восстановления снапшота.
Заключение
CRaC API минимизирует время запуска приложений и снижает потребление ресурсов, не жертвуя возможностями JIT для дальнейшей оптимизации производительности, если это необходимо. CRaC также ускоряет запуск JVM после сбоя или прерывания работы.
С готовыми контейнерами от Axiom вам не нужно настраивать JDK или ОС для работы с CRaC API, что позволяет вам сосредоточиться на внедрении этой функции в рабочие нагрузки.
Получите сборку JDK с поддержкой CRaC, перейдя в Центр загрузок Axiom JDK, или образ контейнера на нашем сайте. Если у вас возникнут вопросы по работе CRaC, то обращайтесь. Наши инженеры будут рады вам помочь!
В следующей статье вы узнаете, как запустить Java-приложение с помощью CRaC в контейнере.