Нативна компіляція у Quarkus – чому це важливо

Всім привіт! З вами другий пост із нашої серії з Quarkus – сьогодні поговоримо про нативну компіляцію.

Нативна компіляція у Quarkus – чому це важливо

Кваркус - це Java-стек, заточений під Кубернетес. І хоча тут, звичайно, багато що ще потрібно зробити, ми добре пропрацювали масу аспектів, включаючи оптимізацію JVM і цілого ряду фреймворків. Однією з особливостей Quarkus, що викликала підвищений інтерес з боку розробників, став комплексний безшовний підхід до перетворення Java-коду в файли для конкретної операційної системи (так звана «нативна компіляція») за аналогією з C і C++, де така компіляція зазвичай відбувається в кінці циклу, що складається зі складання, тестування та розгортання.

І хоча нативна компіляція, як ми покажемо нижче, важлива, треба відзначити, що Quarkus реально добре працює і на звичайнісінькій Java-машині OpenJDK Hotspot завдяки тим поліпшенням продуктивності, які ми реалізували по всьому стеку. Тому нативну компіляцію варто розглядати як додатковий бонус, який можна використати за бажанням чи потребою. Насправді в тому, що стосується нативних образів, Quarkus значною мірою спирається на OpenJDK. А тепло прийнятий розробниками режим dev mode забезпечує практично миттєве тестування змін за рахунок розвинених можливостей динамічного виконання коду, реалізованих Hotspot. Крім того, при створенні нативних образів GraalVM задіяна бібліотека класів OpenJDK та можливості HotSpot.

То навіщо тоді потрібна нативна компіляція, якщо все й так добре оптимізовано? На це запитання ми і намагатимемося відповісти нижче.

Почнемо з очевидного: Red Hat має великий досвід оптимізації JVM, стеків та фреймворків у ході розвитку проекту JBoss, включаючи:

  • Перший сервер додатків для роботи у хмарі на платформі Red Hat OpenShift.
  • Перший сервер додатків для роботи на комп'ютерах Plug PC.
  • Перший сервер додатків для роботи на Raspberry Pi.
  • Цілий ряд проектів, що працюють на пристроях Android.

Ми вже багато років займаємося проблемами запуску Java-додатків у хмарі та на пристроях з обмеженими ресурсами (читай, IoT) та навчилися вичавлювати з JVM максимум у сенсі продуктивності та оптимізації пам'яті. Як і багато інших, ми вже давно працюємо з нативною компіляцією Java-додатків через GCJ, Пташиний, Excelsior JET і навіть Дальвік і відмінно усвідомлюємо плюси та мінуси такого підходу (наприклад, дилему вибору між універсальністю «build once – run-anywhere» і тим, що скомпіловані додатки мають менший розмір і швидше запускаються).

Чому так важливо враховувати ці плюси та мінуси? Тому що в деяких ситуаціях їх співвідношення стає вирішальним.

  • Наприклад, у serverless/керованих подіями середовищах, де послуги просто повинні запускатися у режимі (жорсткого чи м'якого) реального часу, щоб встигати реагувати на події. На відміну від персистентних сервісів, що довго живуть, тут тривалість холодного запуску критично збільшує час відповіді на запит. На запуск JVM все ще йде суттєвий час, і хоча в деяких випадках його можна скоротити суто апаратними методами, різниця між однією секундою і 5 мілісекундами може бути питанням життя і смерті. Так, тут можна погратися зі створенням гарячого резерву Java-машин (що ми, наприклад, зробили при портування OpenWhisk на Knative), але саме по собі це не гарантує достатньої обробки запитів кількості JVM в міру масштабування навантаження. Та й з економічного погляду це, напевно, не найправильніший варіант.
  • Далі, є ще такий часто випливає аспект, як мультитенантність. Незважаючи на те, що JVM за своїми можливостями сильно наблизилися до операційних систем, вони все ще не здатні робити те, до чого ми так звикли в тому ж Linux - ізолювати процеси. Тому збій одного потоку може вивести з ладу всю Java-машину. Багато хто намагається обійти цей недолік тим, що виділяють під додатки кожного користувача окрему JVM, щоб мінімізувати наслідки збою. Це цілком логічно, але погано поєднується із масштабуванням.
  • Крім того, для хмарно-орієнтованих програм важливий такий показник, як щільність сервісів на хості. Перехід на методологію 12 факторів застосування, мікросервіси та Kubernetes збільшує кількість Java-машин на одну програму. Тобто, з одного боку, все це дає еластичність і надійність, але одночасно зростає і витрата базової пам'яті в перерахунку на сервіс, причому частина цих витрат далеко не завжди є необхідною. Статично скомпіловані виконувані файли виграють тут за рахунок різних технік оптимізації, на кшталт низькорівневого dead-code elimination, коли в підсумковий образ включаються ті частини фреймворків (включаючи і сам JDK), які сервіс реально використовує. Тому нативна компіляція Quarkus допомагає щільніше розміщувати екземпляри сервісів на хості без шкоди для безпеки.

Власне, наведених вище аргументів вже достатньо для того, щоб зрозуміти виправданість нативної компіляції з погляду учасників проекту Quarkus. Однак є ще одна, не технічна, але теж важлива причина: в останні роки багато програмістів і компаній-розробників відмовилися від Java на користь нових мов програмування, вважаючи, що Java разом зі своїми JVM, стеками і фреймворками стала занадто ненажерливою в плані пам'яті, надто повільною і т.д.

Однак звичка використовувати той самий інструмент для вирішення будь-яких завдань – це не завжди правильно. Іноді краще зробити крок назад та пошукати щось інше. І якщо Quarkus змушує людей зробити паузу і задуматися, це добре для всієї екосистеми Java. Quarkus втілює новаторський погляд на те, як створювати більш ефективні програми, роблячи Java більш релевантною новим архітектурам додатків, на кшталт serverless. Крім того, завдяки своїй розширюваності, Quarkus, як ми сподіваємося, матиме цілу екосистему Java-розширень, значно збільшивши кількість фреймворків, які з коробки будуть підтримувати нативну компіляцію у складі додатків.

Джерело: habr.com

Додати коментар або відгук