Przejdź do treści

Poradniki

Migracja krytycznej aplikacji z Javy 8 do nowszej Javy

· 6 min czytania

Krytyczną aplikację w Javie 8 aktualizuje się etapami: inwentaryzacja każdej zależności, najpierw przejście na Javę 11, potem na każde kolejne wydanie z długoterminowym wsparciem (LTS), a o tym, kiedy dany krok jest bezpieczny, decydują testy i testy obciążeniowe. Większość pracy dotyczy bibliotek, narzędzi budowania i kodu, który sięga do wnętrza JDK, a nie logiki biznesowej.

Pozostanie na Javie 8 z roku na rok zawęża możliwości

Aplikacja w Javie 8 może działać latami i właśnie w tym tkwi pułapka. Poprawki bezpieczeństwa coraz częściej zależą od płatnej umowy wsparcia lub dystrybucji, która nadal je przenosi, a główne biblioteki poszły dalej. Spring Boot 3 wymaga na przykład co najmniej Javy 17, więc pozostanie na Javie 8 zamraża też wersje frameworka.

Nowsze JVM także lepiej wykonują ten sam kod. G1 jest domyślnym garbage collectorem od Javy 9, kolektory o krótkich pauzach, takie jak ZGC, są gotowe do produkcji, a compact strings zmniejszają zużycie pamięci przy obciążeniach z dużą ilością tekstu. Inżynierowie oczekują też nowoczesnych funkcji języka, takich jak rekordy, bloki tekstowe i wyrażenia switch, przez co trudniej skompletować zespół do bazy kodu w Javie 8.

Najpierw inwentaryzacja wszystkiego, od czego zależy aplikacja

Sama aktualizacja JDK rzadko jest najtrudniejsza. Trudny jest długi ogon bibliotek, wtyczek i agentów, które napisano dla Javy 8 i nigdy nie zaktualizowano. Przed jakąkolwiek zmianą trzeba sporządzić pełną listę i zapisać wersję każdej pozycji.

  • Dostawca i wersja JDK w każdym środowisku, od laptopów programistów po produkcję.
  • Zależności bezpośrednie i przechodnie, wyeksportowane z drzewa zależności Mavena lub raportu zależności Gradle.
  • Wtyczki budowania, generatory kodu i procesory adnotacji, takie jak Lombok.
  • Serwer aplikacji lub kontener serwletów, jeśli aplikacja w nim działa.
  • Agenty Javy do monitorowania, profilowania lub bezpieczeństwa, które głęboko ingerują w JVM.
  • Kod korzystający z wewnętrznych API JDK, wykryty narzędziem jdeps z opcją jdk-internals.
  • Flagi JVM, ustawienia garbage collectora i wszelkie skrypty, które parsują wynik java -version.

Przechodzenie przez kolejne wydania LTS zamiast skoku

Z Javy 8 przechodzi się na 11, potem na 17, a potem na 21 lub 25. Każdy krok ma własny zestaw usunięć i zmian zachowania, a jeden skok miesza je wszystkie w jedną awarię, której nie da się zdiagnozować. Naprawianie jednej warstwy naraz sprawia, że każdy krok jest na tyle mały, by go przejrzeć i wycofać.

Tam, gdzie to możliwe, biblioteki warto zaktualizować jeszcze na Javie 8, bo wiele nowszych wersji obsługuje zarówno stare, jak i nowe JDK. Potem uruchamia się istniejący build na nowej JVM, zanim zmieni się docelową wersję kompilacji. Oddziela to problemy środowiska uruchomieniowego od problemów kompilatora, a flaga kompilatora release pozwala jawnie określić docelowy bytecode.

Zestaw testów jako bramka dla każdego kroku

Testy to jedyny obiektywny sygnał, że zaktualizowana aplikacja nadal zachowuje się tak samo. Jeśli krytyczne ścieżki mają słabe pokrycie, najpierw pisze się testy charakteryzujące: testy, które zapisują, co system robi dziś, łącznie z jego nietypowymi przypadkami brzegowymi, bez oceniania, czy to zachowanie jest poprawne.

Warto dodać testy integracyjne, które działają na prawdziwej bazie danych i prawdziwych brokerach wiadomości, bo wiele awarii po aktualizacji pojawia się dopiero na tych granicach. W systemach z dużą liczbą obliczeń te same dane wejściowe przepuszcza się przez starą i nową wersję i porównuje wyniki pole po polu. Szczególnej uwagi wymagają daty, liczby i tekst: Java 9 domyślnie przeszła na dane regionalne CLDR, a Java 18 ustawiła UTF-8 jako domyślne kodowanie znaków, i obie zmiany mogą po cichu zmienić formatowanie lub parsowanie.

Pułapki, które psują kod w Javie 8

Większość problemów należy do kilku znanych kategorii. Każdej z nich warto poszukać przed aktualizacją, zamiast odkrywać ją na produkcji.

  • Usunięte moduły Java EE i CORBA: Java 11 usunęła z JDK JAXB, JAX-WS, JavaBeans Activation i wspólne adnotacje, więc trzeba je dodać z powrotem jako jawne zależności.
  • Silna enkapsulacja wnętrza JDK: niedozwolony dostęp refleksyjny generował ostrzeżenia od Javy 9, od Javy 16 jest domyślnie blokowany, a od Javy 17 nie można go globalnie ponownie włączyć. Rozwiązaniem jest aktualizacja biblioteki; add-opens należy stosować tylko jako udokumentowany, tymczasowy wyjątek.
  • Przestarzałe narzędzia do bytecode'u: starsze wersje Lomboka, Mockito, Byte Buddy, ASM i wtyczek budowania zawodzą przy nowszych wersjach plików klas.
  • Usunięte funkcje: silnik JavaScript Nashorn usunięto w Javie 15, a Security Manager oznaczono jako przestarzały w Javie 17 i trwale wyłączono w Javie 24.
  • Usunięte flagi JVM: garbage collector CMS usunięto w Javie 14, a JVM uruchomiona z usuniętymi opcjami może odmówić startu.

Wydajność potwierdzona pod realnym obciążeniem, a nie na laptopie

Nowe JDK zmienia garbage collector, kompilator JIT i układ pamięci, więc wydajność może zmienić się w obie strony. Przed każdym krokiem i po nim warto przeprowadzić test obciążeniowy, który odtwarza profil ruchu produkcyjnego, na tym samym sprzęcie lub typie instancji. Porównuje się percentyle opóźnień, przepustowość, pamięć i pauzy garbage collectora, a przed pomiarem trzeba dać kompilatorowi JIT czas na rozgrzanie.

Nasi inżynierowie prowadzili migrację z Javy 8 do 16 krytycznej aplikacji do dziennego handlu gazem dla krajowego działu handlu gazem, pracując za pośrednictwem Sopra Steria. W tym systemie obliczenia przepustowości i przepływów energii musiały pozostać poprawne i szybkie pod obciążeniem o wysokiej częstotliwości, dlatego aktualizacji towarzyszyła optymalizacja tych obliczeń, a jej efekt został potwierdzony pod obciążeniem, a nie założony.

Stopniowe wdrożenie z drogą powrotu

Zaktualizowane środowisko uruchomieniowe najpierw wdraża się na jednej instancji lub dla niewielkiej części ruchu i obserwuje wskaźnik błędów, opóźnienia i zachowanie garbage collectora na tle poziomu bazowego z Javy 8. Poprzedni build powinien pozostać gotowy do wdrożenia, dopóki nowa wersja nie przejdzie przez zwykłe cykle biznesowe, łącznie z końcem miesiąca czy okresami szczytu.

Dopiero wtedy usuwa się artefakty Javy 8 i zaczyna korzystać z nowych funkcji języka. Jeśli potrzebują Państwo pomocy w zaplanowaniu lub przeprowadzeniu takiej aktualizacji, SDK Enterprises zapewnia do niej własnych inżynierów i sprawdzonych specjalistów w ramach jednej umowy z jasną odpowiedzialnością.

Najważniejsze wnioski

  • Nakład pracy przy aktualizacji z Javy 8 dotyczy głównie zależności, narzędzi budowania i korzystania z wnętrza JDK, a nie logiki biznesowej.
  • Przez kolejne wydania LTS przechodzi się po jednym, aby każda awaria miała jedną, łatwą do znalezienia przyczynę.
  • Testy charakteryzujące i integracyjne są bramką dla każdego kroku, zwłaszcza w zakresie dat, liczb i kodowania tekstu.
  • Wydajność weryfikuje się pod obciążeniem zbliżonym do produkcyjnego, bo zmiany w garbage collectorze i JIT mogą przesunąć wyniki w obie strony.
  • Najpierw wdraża się dla niewielkiej części ruchu, a build z Javą 8 pozostaje gotowy do wdrożenia, dopóki nowe środowisko się nie sprawdzi.

FAQ

Czy można przejść bezpośrednio z Javy 8 na Javę 21?

Można, ale wtedy wszystkie zmiany od Javy 9 do 21 przychodzą naraz, co utrudnia śledzenie awarii. Przejście przez 11 i 17 kosztuje nieco więcej czasu budowania, a oszczędza wiele debugowania w krytycznym systemie.

Ile trwa aktualizacja z Javy 8?

To zależy od liczby zależności, tego, ile z nich korzysta z wnętrza JDK, pokrycia testami i zakresu testów obciążeniowych, których wymaga system. Inwentaryzacja i próbne uruchomienie na nowej JVM wcześnie dają realistyczny szacunek, zanim zobowiążą się Państwo do harmonogramu.

Czy trzeba przepisać kod, aby korzystać z nowych funkcji Javy?

Nie. Java zachowuje silną wsteczną kompatybilność dla kodu, który korzysta ze standardowych, nieprzestarzałych API. Rekordy, bloki tekstowe i inne funkcje wprowadza się stopniowo, gdy zaktualizowane środowisko uruchomieniowe działa stabilnie na produkcji.

Prosimy opisać, czego Państwo potrzebują.

Coś do zbudowania, ludzie do znalezienia albo pytanie, na które trzeba odpowiedzieć. W 30-minutowej rozmowie słuchamy i szczerze mówimy, jak możemy pomóc i czego by to wymagało.