W tym roku planujemy poważnie rozwijać temat kontenerów, i . Logicznym rozwinięciem tych tematów będzie opowieść o frameworku Quarkus, który już na Habrze. Dzisiejszy artykuł poświęcony jest nie tyle zasadom działania "subatomowej super szybkie Java", ile perspektywom, które Quarkus wnosi do Enterprise.

Java i JVM wciąż cieszą się ogromną popularnością, ale w pracy z technologiami serverless i mikroserwisami opartymi na chmurze Java i inne języki dla JVM są coraz rzadziej stosowane, ponieważ zajmują zbyt dużo miejsca w pamięci i zbyt wolno się ładować, przez co nie nadają się do użycia z krótkożyjącymi kontenerami. Na szczęście ta sytuacja zaczyna się zmieniać dzięki Quarkus.
Super szybka subatomowa Java weszła na nowy poziom!
42 wydania, 8 miesięcy pracy społeczności i 177 niesamowitych programistów – efektem tego była premiera w listopadzie 2019 roku , wydania, które oznacza ważny kamień milowy w rozwoju projektu i oferuje mnóstwo świetnych funkcji i możliwości (więcej informacji można znaleźć w ).
Dziś opowiemy, jak Quarkus łączy modele programowania imperatywnego i reaktywnego na bazie jednego reaktywnego rdzenia. Rozpoczniemy od krótkiego rysu historycznego, a następnie szczegółowo omówimy, na czym polega dualizm reaktywnego rdzenia Quarkus i jak -programiści mogą skorzystać z tych korzyści.
, i -funkcje – wszystko to dziś, jak się mówi, jest na fali. Ostatnio tworzenie chmurowych architektur stało się znacznie prostsze i bardziej dostępne, jednak problemy pozostały – szczególnie wśród programistów Java. Na przykład, w przypadku funkcji serverless i mikroserwisów istnieje pilna potrzeba skrócenia czasu uruchamiania, zmniejszenia zużycia pamięci i uczynienia ich tworzenia bardziej wygodnym i przyjemnym. Java w ostatnich latach wprowadziła kilka udoskonaleń, takich jak funkcjonalność ergonomii poprawiona dla kontenerów itd. Jednak osiągnięcie prawidłowej pracy Java w kontenerze wciąż jest trudne. Dlatego zaczniemy od omówienia niektórych z wewnętrznych trudności Java, które szczególnie wyraźnie ujawniają się w czasie tworzenia aplikacji Java opartych na kontenerach.
Na początek zwróćmy uwagę na historię.

Wątki i kontenery
Zaczynając od wersji 8u131, Java zaczęła w miarę dobrze wspierać kontenery dzięki ulepszeniom w funkcji ergonomii. W szczególności teraz JVM wie, na ilu rdzeniach procesora jest uruchamiana, i może odpowiednio dostosować pule wątków — zazwyczaj puli fork/join. Oczywiście, to świetnie, ale załóżmy, że mamy tradycyjną aplikację internetową, która korzysta z serwletów HTTP i jest uruchamiana w Tomcat, Jetty itd. W rezultacie ta aplikacja przydzieli oddzielny wątek dla każdego żądania i pozwoli mu zablokować ten wątek podczas oczekiwania na operacje I/O, na przykład podczas dostępu do bazy danych, plików lub innych usług. To oznacza, że rozmiar takiej aplikacji nie zależy od liczby dostępnych rdzeni, lecz od liczby równoczesnych żądań. Ponadto oznacza to, że kwoty lub limity w Kubernetes dotyczące liczby rdzeni nie pomogą w tym przypadku szczególnie, a sprawa zakończy się throttlingiem.
Wycieki pamięci
Wątki to pamięć. A wewnętrzne ograniczenia kontenerów dotyczące pamięci wcale nie są panaceum. Po prostu zacznij zwiększać liczbę aplikacji i wątków, a prędzej czy później napotkasz krytyczny wzrost częstotliwości przełączeń i, w konsekwencji, degradację wydajności. Co więcej, jeśli aplikacja korzysta z tradycyjnych frameworków mikroserwisowych lub łączy się z bazą danych, lub wykorzystuje cache, lub w jakikolwiek inny sposób dodatkowo zużywa pamięć, zdecydowanie potrzebujesz narzędzia, które pozwoli zajrzeć do wnętrza JVM i sprawdzić, jak zarządza pamięcią, przy tym nie zabijając samej JVM (na przykład XX:+UseCGroupMemoryLimitForHeap). I nawet mimo że, od Java 9, JVM nauczyła się kompatybilności z cgroups i odpowiedniego dostosowywania, rezerwacja i zarządzanie pamięcią pozostaje dość skomplikowanym zadaniem.
Kwoty i limity
W Java 11 wprowadzono wsparcie dla kwot CPU (np. PreferContainerQuotaForCPUCount). Kubernetes również oferuje wsparcie dla limitów i kwot. Tak, to wszystko ma sens, ale jeśli aplikacja znów wychodzi poza przydzieloną kwotę, ponownie dochodzimy do sytuacji, że rozmiar, jak w przypadku tradycyjnych aplikacji Java, determinowany jest przez liczbę rdzeni i z przydzieleniem oddzielnego wątku dla każdego żądania, co oznacza, że niewiele z tego można skorzystać.
Ponadto, korzystając z kwot i limitów lub funkcji poziomego (scale-out) skalowania platformy opartej na Kubernetes, problem nie rozwiązuje się sam. Po prostu wydajemy więcej zasobów na rozwiązanie pierwotnego problemu, co w końcu prowadzi do nadmiernego zużycia zasobów. A jeśli jest to system o dużym obciążeniu w publicznej chmurze, niemal na pewno zaczynamy zużywać więcej zasobów, niż jest to rzeczywiście potrzebne.
I co z tym wszystkim zrobić?
Mówiąc prosto, należy korzystać z asynchronicznych i nieblokujących bibliotek wejścia/wyjścia oraz frameworków takich jak Netty, lub Akka. Są one znacznie lepiej dostosowane do pracy w kontenerach dzięki swojej reaktywnej naturze. Dzięki nieblokującemu wejściu/wyjściu, ten sam wątek może obsługiwać jednocześnie wiele równoległych zapytań. Podczas gdy jedno zapytanie czeka na wyniki wejścia/wyjścia, wątek je obsługujący jest zwalniany i przechodzi do obsługi innego zapytania. A kiedy wyniki wejścia/wyjścia w końcu przychodzą, przetwarzanie pierwszego zapytania jest kontynuowane. Przeplatając przetwarzanie zapytań w ramach tego samego wątku, można zmniejszyć łączną liczbę wątków i zredukować zużycie zasobów na przetwarzanie zapytań.
W przypadku nieblokującego wejścia/wyjścia liczba rdzeni staje się kluczowym parametrem, ponieważ to właśnie ona określa liczbę wątków wejścia/wyjścia, które mogą być wykonywane równolegle. Przy odpowiednim wykorzystaniu pozwala to efektywnie rozkładać obciążenie wśród rdzeni i radzić sobie z większymi obciążeniami przy mniejszych zasobach.
Jak, to wszystko?
Nie, jest jeszcze coś. Programowanie reaktywne pomaga lepiej wykorzystać zasoby, ale też ma swoją cenę. W szczególności, kod będzie musiał być przepisany zgodnie z zasadami nieblokowania i unikać blokowania wątków wejścia/wyjścia. A to zupełnie inny model rozwoju i wykonania. I chociaż istnieje wiele użytecznych bibliotek, to wciąż jest to radykalna zmiana w sposobie myślenia.
Po pierwsze, musisz nauczyć się pisać kod, który działa asynchronicznie. Gdy zaczynasz korzystać z nieblokującego wejścia-wyjścia, musisz wyraźnie określić, co ma się stać po otrzymaniu odpowiedzi na zapytanie. Nie można już po prostu blokować i czekać. Zamiast tego możesz przekazywać wywołania zwrotne, korzystać z programowania reaktywnego lub kontynuacji. Ale to nie wszystko: aby korzystać z nieblokującego wejścia-wyjścia, potrzebujesz zarówno nieblokujących serwerów, jak i klientów, i najlepiej w każdej sytuacji. W przypadku HTTP wszystko jest proste, ale są też bazy danych, systemy plików i wiele więcej.
I chociaż całkowita reakcja skrajna zapewnia maksymalną efektywność, takie przesunięcie może być trudne do przełknięcia w praktyce. Dlatego możliwość łączenia kodu reaktywnego i imperatywnego staje się warunkiem koniecznym, aby:
- Efektywnie wykorzystywać zasoby w najbardziej obciążonych kierunkach systemu oprogramowania;
- Używać prostszego stylowo kodu w pozostałych częściach.
Przedstawiamy Quarkus
Właściwie to jest sedno Quarkus – połączenie modeli reaktywnych i imperatywnych w ramach jednego środowiska wykonawczego.
Na bazie Quarkus leżą Vert.x i Netty, na których zbudowano szereg reaktywnych frameworków i rozszerzeń mających na celu pomoc deweloperowi. Quarkus jest przeznaczony do budowy nie tylko mikrousług HTTP, ale także architektur opartych na zdarzeniach. Dzięki swojej reaktywnej naturze, bardzo efektywnie współpracuje z systemami wymiany wiadomości (Apache Kafka, AMQP itd.).
Cała sztuczka polega na tym, jak używać tego samego reaktywnego silnika zarówno dla kodu imperatywnego, jak i reaktywnego.

Quarkus radzi sobie z tym doskonale. Wybór między programowaniem imperatywnym a reaktywnym jest oczywisty – należy używać reaktywnego rdzenia zarówno do jednego, jak i drugiego. A w czym to naprawdę pomaga, to szybki kod nieblokujący, który przetwarza prawie wszystko, co przechodzi przez pętlę zdarzeń (event-loop thread, znaną też jako IO thread). Ale jeśli masz klasyczne aplikacje REST lub aplikacje po stronie klienta, Quarkus ma dla ciebie gotowy imperatywny model programowania. Na przykład wsparcie dla HTTP w Quarkus opiera się na używaniu nieblokującego i reaktywnego silnika (Eclipse Vert.x i Netty). Wszystkie zapytania HTTP, które otrzymuje twoja aplikacja, najpierw przechodzą przez pętlę zdarzeń (IO Thread), a następnie są przesyłane do tej części kodu, która zarządza zapytaniami. W zależności od miejsca docelowego kod zarządzający zapytaniami może być wywoływany w ramach osobnego wątku (tzw. worker thread, używane w przypadku serwletów i Jax-RS) lub korzystać z pierwotnego wątku wejścia-wyjścia (reaktywny szlak reactive route).

Dla łączników systemów przesyłania wiadomości używane są nieblokujące klienty, które działają na silniku Vert.x. Dzięki temu możesz efektywnie wysyłać, odbierać i przetwarzać wiadomości z systemów klasy messaging middleware.
Na stronie zebrano kilka dobrych przewodników, które pomogą rozpocząć pracę z Quarkus:
Ponadto przygotowaliśmy internetowe praktyczne lekcje, które wprowadzają w różne aspekty reaktywnego programowania; do ich odbywania wystarczy tylko przeglądarka, nie jest wymagana żadna IDE, a komputer nie jest konieczny. Te lekcje można znaleźć .
Przydatne zasoby
- Strona projektu Quarkus –
- Projekt Quarkus na GitHubie –
- Twitter projektu Quarkus –
- Czat projektu Quarkus –
- Fora projektu Quarkus – !forum/quarkus-dev
10 samouczków wideo o Quarkus, aby zapoznać się z tematem
Jak piszą na stronie , – to -oryjentowany Java-Stack dostosowany do GraalVM oraz OpenJDK HotSpot, zbudowany z najlepszych bibliotek i standardów Java.
Aby pomóc ci zrozumieć temat, wybraliśmy 10 samouczków wideo, które omawiają różne aspekty Quarkus i przykłady jego zastosowania:
1. Przedstawiamy Quarkus: framework Java nowej generacji dla Kubernetes
Autorzy: Thomas Qvarnstrom i Jason Greene
Celem projektu Quarkus jest stworzenie platformy Java dla Kubernetes i środowisk serverless, a także połączenie modeli programowania reaktywnego i imperatywnego w ramach jednego środowiska wykonawczego, aby deweloperzy mogli elastycznie dobierać podejście do pracy z szerokim zakresem rozproszonych architektur aplikacji. Dowiedz się więcej z poniższego wykładu wprowadzającego.

2. Quarkus: super szybka subatomowa Java
Autor: Burr Sutter
Film instruktażowy z internetowego wykładu DevNation Live pokazuje, jak wykorzystać Quarkus do optymalizacji korporacyjnych aplikacji Java, API, mikroserwisów oraz funkcji serverless w środowisku Kubernetes/OpenShift, czyniąc je znacznie mniejszymi, szybszymi i bardziej skalowalnymi.

3. Quarkus i GraalVM: przyspieszamy Hibernate do ekstremalnych prędkości i ściskamy do subatomowych rozmiarów
Autor: Sanne Grinovero
Z prezentacji dowiesz się, jak powstał Quarkus, jak działa i jak umożliwia tworzenie skomplikowanych bibliotek, takich jak Hibernate ORM, zgodnych z natywnymi obrazami GraalVM.

4. Uczymy się rozwijać aplikacje serverless
Autor: Marthen Luther
W poniższym filmie pokazano, jak stworzyć prostą aplikację Java za pomocą Quarkus i wdrożyć ją jako aplikację serverless na Knative.

5. Quarkus: programuj z przyjemnością
Autor: Edson Yanaga
Filmowy przewodnik po tworzeniu pierwszego projektu Quarkus, który pozwala zobaczyć, dlaczego Quarkus zdobywa serca deweloperów.

6. Java i kontenery – jak będzie wyglądać ich wspólna przyszłość
Autor: Mark Little
Ta prezentacja przedstawia historię Javy i wyjaśnia, dlaczego Quarkus jest przyszłością Javy.

7. Quarkus: super szybka subatomowa Java
Autor: Dimitris Andreadis
Przegląd korzyści Quarkus, które zdobyły uznanie deweloperów: prostota, ekstremalne prędkości, najlepsze biblioteki i standardy.

8. Quarkus i subatomowe systemy reaktywne
Autor: Clement Escoffier
Dzięki integracji z GraalVM Quarkus zapewnia superszybkie doświadczenie w rozwijaniu oraz subatomowe środowisko wykonawcze. Autor mówi o reaktywnej stronie Quarkusa i jak z niej korzystać przy tworzeniu aplikacji reaktywnych i aplikacji z przesyłaniem strumieniowym.

9. Quarkus i szybkie tworzenie aplikacji w Eclipse MicroProfile
Autor: John Clingan
Łącząc Eclipse MicroProfile z Quarkus, programiści mogą tworzyć w pełni funkcjonalne aplikacje kontenerowe MicroProfile, które uruchamiają się w ciągu kilku milisekund. Film szczegółowo omawia, jak kodować aplikację kontenerową MicroProfile do wdrożenia na platformie Kubernetes.

10. Java, wersja „Turbo”
Autor: Marcus Biel
Autor pokazuje, jak wykorzystać Quarkus do tworzenia supermałych i superszybkich kontenerów Java, co pozwala na prawdziwy przełom, szczególnie w środowiskach serverless.

Źródło: habr.com
