Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Witam wszystkich na tym blogu, oto czwarty post z serii Quarkus!

Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Poprzedni wpis dotyczyło tego, jak Quarkus łączy MicroProfile i Spring. Przypomnijmy Ci to kwarkus jest pozycjonowany jako „ultraszybka subatomowa Java”, czyli „stos Java zorientowany na Kubernetes, dostosowany do GraalVM i OpenJDK HotSpot i złożony z najlepszych bibliotek i standardów”. Dziś na przykładzie pokażemy jak zmodernizować istniejące aplikacje Java wykorzystując możliwości Quarkus aplikacje helloworld z repozytorium szybkiego startu Red Hat JBoss Enterprise Application Platform (JBoss EAP), który wykorzystuje technologie CDI i Servlet 3 wspierane przez Quarkus.

Należy tutaj zauważyć, że zarówno Quarkus, jak i JBoss EAP kładą nacisk na używanie narzędzi możliwie najbardziej opartych na standardach. Nie masz aplikacji działającej na JBoss EAP? Nie ma problemu, można go łatwo przenieść z bieżącego serwera aplikacji do JBoss EAP za pomocą Zestaw narzędzi do migracji aplikacji Red Hat. Po czym ostateczna i działająca wersja zmodernizowanego kodu będzie dostępna w repozytorium github.com/mrizzi/jboss-eap-quickstarts/tree/quarkus, w module Witaj świecie.

Pisząc ten post, użyliśmy tutoriale Quarkus'a, głównie Tworzenie pierwszej aplikacji i Budynek A Natywny plik wykonywalny.

Zdobądźmy kod

Na początek utwórzmy lokalny klon repozytorium Szybkie starty JBoss EAP:

$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Cloning into 'jboss-eap-quickstarts'...
remote: Enumerating objects: 148133, done.
remote: Total 148133 (delta 0), reused 0 (delta 0), pack-reused 148133
Receiving objects: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, done.
Resolving deltas: 100% (66476/66476), done.
$ cd jboss-eap-quickstarts/helloworld/

Zobaczmy jak działa oryginalny helloworld

Właściwie istota tej aplikacji jest jasna z nazwy, ale jej kod zmodernizujemy ściśle naukowo. Dlatego najpierw przyjrzyjmy się tej aplikacji w jej oryginalnej formie.

Wdrażanie helloworld

1. Otwórz terminal i przejdź do katalogu głównego folderu JBoss EAP (możesz go pobrać tutaj), czyli do folderu EAP_HOME.

2. Uruchom serwer JBoss EAP z profilem domyślnym:

$ EAP_HOME/bin/standalone.sh

Uwaga: na Windows для запуска используется сценарий EAP_HOMEbinstandalone.bat.

Po kilku sekundach w logu powinno pojawić się coś takiego:

[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) started in 3315ms - Started 306 of 527 services (321 services are lazy, passive or on-demand)

3. Otwórz w przeglądarce 127.0.0.1:8080 i widzimy to:

Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Ryż. 1. Strona główna JBoss EAP.

4. Postępuj zgodnie z instrukcjami zawartymi w instrukcji Kompiluj i wdrażaj przewodnik Szybki Start: rozwiń helloworld i uruchom (z folderu głównego projektu) następującą komendę:

$ mvn clean install wildfly:deploy

Po pomyślnym wykonaniu tego polecenia w dzienniku zobaczymy coś takiego:

[INFO] ------------------------------------------------------------------------ 
[INFO] BUILD SUCCESS 
[INFO] ------------------------------------------------------------------------ 
[INFO] Total time: 8.224 s

Tak więc pierwsze wdrożenie aplikacji helloworld na JBoss EAP zajęło nieco ponad 8 sekund.

Testuję helloworld

Postępowanie ściśle według instrukcji Uzyskaj dostęp do aplikacji, otwórz w przeglądarce 127.0.0.1:8080/helloworld i widzimy to:

Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Ryż. 2. Oryginalny Hello World od JBoss EAP.

Dokonywanie zmian

Zmień parametr wejściowy createHelloMessage(String name) z World na Marco:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Uruchom ponownie następujące polecenie:

$ mvn clean install wildfly:deploy

Następnie odświeżamy stronę w przeglądarce i widzimy, że tekst się zmienił:

Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Ryż. 3. Witaj Marco w JBoss EAP.

Wycofaj wdrożenie helloworld i zamknij JBoss EAP

Jest to opcjonalne, ale jeśli chcesz anulować wdrożenie, możesz to zrobić za pomocą następującego polecenia:

$ mvn clean install wildfly:undeploy

Aby zamknąć instancję JBoss EAP, po prostu naciśnij Ctrl+C w oknie terminala.

Aktualizacja helloworld

Zmodernizujmy teraz oryginalną aplikację helloworld.

Utwórz nowy oddział

Po zakończeniu projektu szybkiego startu tworzymy nową gałąź roboczą:

$ git checkout -b quarkus 7.2.0.GA

Zmiana pliku pom.xml

Zmianę aplikacji zaczniemy od pliku pom.xml. Aby umożliwić Quarkusowi wstawianie do niego bloków XML, uruchom następujące polecenie w folderze helloworld:

$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create

Podczas pisania tego artykułu korzystano z wersji 0.23.2. Quarkus często wypuszcza nowe wersje, o tym, która wersja jest najnowsza, można dowiedzieć się na stronie internetowej github.com/quarkusio/quarkus/releases/latest.

Powyższe polecenie wstawi następujące elementy do pom.xml:

  • Nieruchomość , który określa wersję programu Quarkus, której należy używać.
  • Blok zaimportować BOM Quarkus (zestawienie materiałów), aby nie dodawać wersji dla każdej zależności Quarkus.
  • Za spakowanie aplikacji i udostępnienie trybu deweloperskiego odpowiada wtyczka quarkus-maven.
  • Natywny profil do tworzenia plików wykonywalnych aplikacji.

Dodatkowo ręcznie wprowadzamy następujące zmiany w pliku pom.xml:

  1. Wyciągnięcie znacznika z bloku i umieść go nad znacznikiem . Ponieważ w następnym kroku usuniemy blok , to musisz zapisać .
  2. Usuwanie bloku , ponieważ podczas pracy z Quarkusem ta aplikacja nie będzie już potrzebować nadrzędnego pom od JBossa.
  3. Dodać etykietę i umieść go pod znacznikiem . Możesz określić żądany numer wersji.
  4. Usunięcie znacznika , ponieważ ta aplikacja nie jest już wojną, ale zwykłym plikiem JAR.
  5. Modyfikujemy następujące zależności:
    1. Zmień zależność javax.enterprise:cdi-api na io.quarkus:quarkus-arc, usuwając pod warunkiem, że , ponieważ (zgodnie z dokumentacją) to rozszerzenie Quarkus zapewnia wstrzykiwanie zależności CDI.
    2. Zmień zależność org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec na io.quarkus:quarkus-undertow, usuwając pod warunkiem, że , ponieważ (zgodnie z dokumentacją) to rozszerzenie Quarkus zapewnia obsługę serwletów.
    3. Usuwamy zależność org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, ponieważ zawiera ona zależności, które właśnie zmieniliśmy.

Wersja pliku pom.xml ze wszystkimi zmianami znajduje się pod adresem github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml.

Należy zauważyć, że powyższe polecenie mvn io.quarkus:quarkus-maven-plugin:0.23.2:create nie tylko zmienia plik pom.xml, ale także dodaje do projektu szereg komponentów, a mianowicie następujące pliki i foldery:

  • Plik mvnw i mvnw.cmd oraz folder .mvn: Maven Wrapper umożliwia uruchamianie projektów Maven danej wersji Mavena bez konieczności instalowania tej wersji.
  • Folder Docker (w katalogu src/main/): Zawiera przykładowe pliki Dockerfile dla trybów natywnych i jvm (wraz z plikiem .dockerignore).
  • Folder Resources (w katalogu src/main/): Zawiera pusty plik application.properties i przykładową stronę startową Quarkus Index.html (więcej szczegółów można znaleźć w sekcji Uruchamianie zmodernizowanego helloworld).

Uruchom helloworld
Do testowania aplikacji używamy programu quarkus:dev, który uruchamia Quarkus w trybie deweloperskim (więcej szczegółów znajdziesz w tym rozdziale instrukcji Tryb rozwoju).

Uwaga: Ten krok prawdopodobnie zakończy się błędem, ponieważ nie wprowadziliśmy jeszcze wszystkich niezbędnych zmian.

Teraz uruchommy polecenie, aby zobaczyć, jak to działa:

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] Beginning quarkus augmentation
INFO  [org.jbo.threads] JBoss Threads version 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualifiers=[@Default, @Any], target=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
	at io.quarkus.arc.processor.BeanDeployment.processErrors(BeanDeployment.java:841)
	at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:214)
	at io.quarkus.arc.processor.BeanProcessor.initialize(BeanProcessor.java:106)
	at io.quarkus.arc.deployment.ArcProcessor.validate(ArcProcessor.java:249)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at io.quarkus.deployment.ExtensionLoader$1.execute(ExtensionLoader.java:780)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:415)
	at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
	at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426)
	at java.lang.Thread.run(Thread.java:748)
	at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualifiers=[@Default, @Any], target=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
	at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:428)
	at io.quarkus.arc.processor.BeanInfo.init(BeanInfo.java:371)
	at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:206)
	... 14 more

Więc to nie działa... Dlaczego?

Wyjątek UnsatisfiedResolutionException wskazuje na klasę HelloService, która jest członkiem klasy HelloWorldServlet (element Java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). Problem polega na tym, że HelloWorldServlet potrzebuje wstrzykniętej instancji HelloService i nie można jej znaleźć (mimo że obie te klasy znajdują się w tym samym pakiecie).

Czas wrócić do dokumentacja i przeczytaj jak to działa w Quarkusie Wstrzyknąć, i dlatego Konteksty i wstrzykiwanie zależności (CDI). Dlatego otwórz przewodnik po kontekstach i wstrzykiwaniu zależności oraz w sekcji Odkrycie fasoli czytamy: „Klasa komponentu bean, która nie posiada adnotacji definiującej komponent bean, nie jest przeszukiwana.”

Przyjrzyjmy się klasie HelloService – faktycznie nie ma ona takiej adnotacji. Dlatego należy go dodać, aby Quarkus mógł przeszukać i znaleźć fasolę. A ponieważ jest to obiekt bezstanowy, możemy łatwo dodać adnotację @ApplicationScoped w następujący sposób:

@ApplicationScoped
public class HelloService {

Uwaga: tutaj środowisko programistyczne może poprosić Cię o dodanie wymaganego pakietu (patrz linia poniżej), a będziesz musiał to zrobić ręcznie, w ten sposób:

import javax.enterprise.context.ApplicationScoped;

Jeśli masz wątpliwości, jakiego zakresu użyć w przypadku, gdy w ogóle nie jest on określony dla źródłowego komponentu bean, zapoznaj się z dokumentacją JSR 365: Konteksty i wstrzykiwanie zależności dla Java 2.0 — zakres domyślny.

Teraz ponownie próbujemy uruchomić aplikację za pomocą polecenia ./mvnw kompilacja quarkus:dev:

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/target/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 576ms
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 1.083s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Teraz wszystko przebiega bez błędów.

Uruchomienie zmodernizowanego helloworld
Jak zapisano w logu, otwórz go w przeglądarce 0.0.0.0:8080 (domyślna strona startowa Quarkus) i widzimy to:

Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Ryż. 4. Strona startowa dewelopera Quarkus.

Adnotacja WebServlet dla tej aplikacji zawiera następującą definicję kontekstu:

@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {

Dlatego wchodzimy do przeglądarki 0.0.0.0:8080/HelloWorld i widzimy co następuje:

Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Ryż. 5: Strona deweloperska Quarkus dla aplikacji Hello World.

Cóż, wszystko działa.

Teraz dokonajmy zmian w kodzie. Należy zauważyć, że polecenie ./mvnw Compare quarkus:dev nadal działa i nie mamy zamiaru go zatrzymywać. Teraz spróbujmy zastosować te same – bardzo trywialne – zmiany w samym kodzie i zobaczmy, jak Quarkus ułatwia życie programiście:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Zapisz plik, a następnie odśwież stronę internetową, aby zobaczyć Hello Marco, jak pokazano na zrzucie ekranu poniżej:

Quarkus: Modernizacja aplikacji na przykładzie Helloworld z JBoss EAP Quickstart

Ryż. 6. Strona Hello Marco w Quarkus dev.

Sprawdźmy teraz wynik w terminalu:

INFO  [io.qua.dev] (vert.x-worker-thread-3) Changed source files detected, recompiling [/home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/src/main/java/org/jboss/as/quickstarts/helloworld/HelloWorldServlet.java]
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus stopped in 0.003s
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Quarkus augmentation completed in 232ms
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 started in 0.257s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (vert.x-worker-thread-3) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (vert.x-worker-thread-3) Installed features: [cdi]
INFO  [io.qua.dev] (vert.x-worker-thread-3) Hot replace total time: 0.371s

Odświeżenie strony spowodowało wykrycie zmian w kodzie źródłowym, a Quarkus automatycznie wykonał procedurę stop-start. A wszystko to odbyło się w zaledwie 0.371 sekundy (oto właśnie ta „ultraszybka subatomowa Java”).

Budowanie helloworld w pakiecie JAR
Teraz, gdy kod działa tak, jak powinien, spakujmy go za pomocą następującego polecenia:

$ ./mvnw clean package

To polecenie tworzy dwa pliki JAR w folderze /target: plik helloworld-.jar, który jest standardowym artefaktem złożonym przez zespół Mavena wraz z klasami i zasobami projektu. Oraz plik helloworld-runner.jar, który jest wykonywalnym plikiem JAR.

Należy pamiętać, że nie jest to plik uber-jar, ponieważ wszystkie zależności są po prostu kopiowane do folderu /target/lib (nie są pakowane do pliku JAR). Dlatego, aby uruchomić ten plik JAR z innego folderu lub na innym hoście, musisz skopiować zarówno sam plik JAR, jak i folder /lib, biorąc pod uwagę, że element Class-Path w pliku MANIFEST.MF w pakiecie JAR zawiera wyraźna lista plików JAR z folderów lib
Aby dowiedzieć się jak tworzyć aplikacje uber-jar, zapoznaj się z tutorialem Tworzenie Uber-Jara.

Uruchom helloworld spakowany w formacie JAR

Teraz możemy uruchomić nasz plik JAR za pomocą standardowego polecenia Java:

$ java -jar ./target/helloworld-<version>-runner.jar
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.673s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Po wykonaniu tej czynności przejdź do przeglądarki pod adresem 0.0.0.0:8080 i sprawdź czy wszystko działa jak należy.

Kompilowanie helloworld do natywnego pliku wykonywalnego

Zatem nasz helloworld działa jako samodzielna aplikacja Java, korzystając z zależności Quarkus. Możesz jednak pójść dalej i przekształcić go w natywny plik wykonywalny.

Instalowanie GraalVM
Przede wszystkim w tym celu musisz zainstalować niezbędne narzędzia:

1. Pobierz GraalVM 19.2.0.1 z github.com/oracle/graal/releases/tag/vm-19.2.0.1.

2. Rozwiń pobrane archiwum:

$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz

3. Przejdź do folderu untar.

4. Uruchom poniższe polecenie, aby pobrać i dodać obraz natywny:

$ ./bin/gu install native-image

5. Zarejestruj folder utworzony w kroku 2 w zmiennej środowiskowej GRAALVM_HOME:

$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)

Więcej informacji i instrukcje instalacji w innych systemach operacyjnych można znaleźć w instrukcji Tworzenie natywnego pliku wykonywalnego — wymagania wstępne.

Kompilowanie helloworld do natywnego pliku wykonywalnego
Czytanie instrukcji Tworzenie natywnego pliku wykonywalnego — tworzenie natywnego pliku wykonywalnego: „Teraz utwórzmy natywny plik wykonywalny dla naszej aplikacji, aby skrócić czas jej uruchamiania i rozmiar dysku. Plik wykonywalny będzie zawierał wszystko, co niezbędne do uruchomienia aplikacji, w tym JVM (a raczej jej okrojoną wersję, zawierającą tylko to, co jest wymagane do uruchomienia aplikacji) i samą naszą aplikację.

Aby utworzyć natywny plik wykonywalny, musisz włączyć natywny profil Mavena:

$ ./mvnw package -Pnative

Nasza kompilacja zajęła minutę i 10 sekund, a ostateczny plik helloworld — runner f został utworzony w folderze /target.

Uruchom natywny plik wykonywalny helloworld

W poprzednim kroku otrzymaliśmy plik wykonywalny /target/helloworld—runner. Teraz uruchommy to:

$ ./target/helloworld-<version>-runner
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.006s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Otwórz go ponownie w przeglądarce 0.0.0.0:8080 i sprawdź czy wszystko działa jak należy.

Prezentacja zestawienia!

Uważamy, że omówiona w tym poście metoda modernizacji aplikacji Java z wykorzystaniem możliwości Quarkus (aczkolwiek na prostym przykładzie) powinna być aktywnie wykorzystywana w prawdziwym życiu. Robiąc to, prawdopodobnie napotkasz szereg problemów, które częściowo omówimy w następnym poście, gdzie porozmawiamy o tym, jak mierzyć zużycie pamięci, aby ocenić poprawę wydajności, co jest ważną częścią całego procesu modernizacji aplikacji.

Źródło: www.habr.com

Kup niezawodny hosting dla stron z ochroną DDoS, serwery VPS VDS 🔥 Kup niezawodny hosting stron internetowych z ochroną DDoS, serwery VPS VDS | ProHoster