Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Salute à tutti nant'à stu blog, eccu u quartu post di a serie Quarkus !

Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Post precedente era circa cumu Quarkus combina MicroProfile è Spring. Lasciamu ricurdà chì quarkus hè posizionatu cum'è "Java subatomica ultra-veloce", aka "stack Java orientatu à Kubernetes, adattatu per GraalVM è OpenJDK HotSpot è assemblatu da e migliori librerie è standard". Oghje vi mustraremu cumu mudernizà l'applicazioni Java esistenti cù e capacità di Quarkus, usendu l'esempiu applicazioni helloworld da u repository Quickstart Red Hat JBoss Enterprise Application Platform (JBoss EAP), chì usa e tecnulugia CDI è Servlet 3 supportate da Quarkus.

Hè impurtante di nutà quì chì Quarkus è JBoss EAP enfatizzanu l'usu di strumenti chì sò basati in standard quant'è pussibule. Ùn avete micca una applicazione in esecuzione in JBoss EAP? Nisun prublema, pò esse facilmente migratu da u vostru servitore di l'applicazione attuale à JBoss EAP usendu Toolkit di migrazione di l'applicazioni Red Hat. Dopu chì a versione finale è di travagliu di u codice mudernizatu serà dispunibule in u repository github.com/mrizzi/jboss-eap-quickstarts/tree/quarkus, in u modulu ciao mondu.

Quandu scrivite stu post avemu usatu Manuali Quarkus, in fondu Crea a vostra prima applicazione è Building a Eseguibile nativu.

Andemu u codice

Prima di tuttu, creemu un clone locale di u repository JBoss EAP quickstarts:

$ 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/

Videmu cumu funziona u helloworld originale

In verità, l'essenza di sta applicazione hè chjaru da u nome, ma mudernizà u so codice strettamente scientificu. Dunque, prima, fighjemu sta dumanda in a so forma originale.

Implantazione di helloworld

1. Aprite un terminal è andate à a ràdica di u cartulare JBoss EAP (pudete scaricà ccà), vale à dì à u cartulare EAP_HOME.

2. Lanciate u servitore JBoss EAP cù u prufilu predeterminatu:

$ EAP_HOME/bin/standalone.sh

Nutate bè: nantu Windows U script EAP_HOMEbinstandalone.bat hè utilizatu per u lanciu.

Dopu un coppiu di sicondi, qualcosa cum'è questu deve esse appare in u logu:

[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. Open in un navigatore 127.0.0.1:8080 è vedemu questu:

Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Risu. 1. JBoss EAP Home Page.

4. Segui i instructions in u manual Custruite è implementate u Quickstart: espansione helloworld è eseguite (da u cartulare root di u prughjettu) u cumandimu seguente:

$ mvn clean install wildfly:deploy

Dopu avè eseguitu bè stu cumandamentu, vedemu qualcosa cum'è questu in u logu:

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

Dunque, a prima implementazione di l'applicazione helloworld nantu à JBoss EAP hà pigliatu pocu più di 8 seconde.

Testu Helloworld

Agisce strettamente secondu l'istruzzioni Accedi à l'Applicazione, apre in u navigatore 127.0.0.1:8080/helloworld è vedemu questu:

Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Risu. 2. Original Hello World da JBoss EAP.

Fendu cambiamenti

Cambia u paràmetru di input createHelloMessage(String name) da World à Marco:

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

Eseguite u cumandimu seguitu di novu:

$ mvn clean install wildfly:deploy

Allora rinfrescemu a pagina in u navigatore è vedemu chì u testu hè cambiatu:

Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Risu. 3. Hello Marco in JBoss EAP.

Ritirate l'implementazione di helloworld è chjude JBoss EAP

Questu hè facultativu, ma se vulete annullà a implementazione, pudete fà cusì cù u cumandimu seguente:

$ mvn clean install wildfly:undeploy

Per chjude a vostra istanza JBoss EAP, basta appughjà Ctrl + C in a finestra di u terminal.

Aghjurnamentu di Helloworld

Avà mudernizemu l'applicazione originale helloworld.

Crea un novu ramu

Creemu un novu ramu di travagliu dopu chì u prughjettu di avviu rapidu hè finitu:

$ git checkout -b quarkus 7.2.0.GA

Cambia u schedariu pom.xml

Avemu da cumincià à cambià l'applicazione da u schedariu pom.xml. Per permette à Quarkus di inserisce blocchi XML in questu, eseguite u cumandimu seguitu in u cartulare helloworld:

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

Quandu scrivite stu articulu, a versione 0.23.2 hè stata utilizata. Quarkus spessu libera novi versioni, pudete scopre quale versione hè l'ultima in u situ web github.com/quarkusio/quarkus/releases/latest.

U cumandimu sopra inserisce i seguenti elementi in pom.xml:

  • Pruprietà , chì specifica a versione di Quarkus à aduprà.
  • Bloccu per impurtà Quarkus BOM (bill of materials), per ùn aghjunghje una versione per ogni dependenza di Quarkus.
  • U quarkus-maven-plugin hè rispunsevule per imballà l'applicazione è furnisce u modu di sviluppu.
  • U prufilu nativu per creà eseguibili di l'applicazione.

Inoltre, facemu manualmente i seguenti cambiamenti à pom.xml:

  1. Tirà fora l'etichetta da u bloccu è mette sopra à l'etichetta . Perchè in u prossimu passu avemu da sguassà u bloccu , allura vi tocca à salvà .
  2. Eliminazione di un bloccu , perchè quandu eseguite cù Quarkus, sta applicazione ùn hà più bisognu di un pom parent da JBoss.
  3. Aghjunghjite un tag è mette sottu à u tag . Pudete specificà u numeru di versione chì vulete.
  4. Eliminazione di l'etichetta , postu chì sta applicazione ùn hè più una GUERRA, ma un JAR regular.
  5. Modifichemu e seguenti dipendenze:
    1. Cambia a dependenza javax.enterprise:cdi-api à io.quarkus:quarkus-arc, eliminendu furnitu , postu chì (sicondu i documenti) sta estensione Quarkus furnisce l'iniezione di dipendenze CDI.
    2. Cambia a dipendenza org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec à io.quarkus:quarkus-undertow, rimuovendu furnitu , perchè (sicondu i documenti) sta estensione Quarkus furnisce supportu per i servlets.
    3. Rimuovemu a dependenza org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec postu chì vene cù e dipendenze chì avemu appena cambiatu.

A versione di u schedariu pom.xml cù tutti i cambiamenti si trova à github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml.

Innota chì u mvn io.quarkus:quarkus-maven-plugin:0.23.2:create command above cambia micca solu u schedariu pom.xml, ma ancu aghjunghje una quantità di cumpunenti à u prugettu, vale à dì i seguenti schedari è cartulare:

  • U schedariu mvnw è mvnw.cmd è u cartulare .mvn: Maven Wrapper vi permette di eseguisce prughjetti Maven di una versione di Maven senza installà quella versione.
  • Docker folder (in u cartulare src/main/): Questu cuntene esempi di Dockerfiles per i modi nativi è jvm (inseme cù u schedariu .dockerignore).
  • Cartulare di risorse (in u cartulare src/main/): Questu cuntene un schedariu application.properties viotu è una pagina iniziale di Quarkus index.html (vede Run the helloworld mudernizatu per più dettagli).

Lanciari helloworld
Per pruvà l'applicazione, usemu quarkus:dev, chì lancia Quarkus in modu di sviluppu (per più dettagli, vede sta sezione in u manuale Modu di sviluppu).

Nutate bè: Stu passu s'aspittava risultà in un errore, postu chì ùn avemu micca ancu fattu tutti i cambiamenti necessarii.

Avà eseguisce u cumandamentu per vede cumu funziona:

$ ./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

Allora, ùn viaghja micca... Perchè ?

L'UnsatisfiedResolutionException punta à a classe HelloService, chì hè un membru di a classe HelloWorldServlet (membru java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). U prublema hè chì HelloWorldServlet hà bisognu di una istanza injected di HelloService, è ùn si pò truvà (ancu s'è e duie classi sò in u stessu pacchettu).

Hè u tempu di vultà ducumentazione è leghje cumu funziona in Quarkus Injectà, è dunque Cuntesti è Injezione di Dipendenza (CDI). Dunque, apre a guida di Cuntestazioni è Injezione di Dipendenza è in a sezione A scuperta di fagioli leghjemu: "Una classa di fagioli chì ùn hà micca una annotazione di definizione di fagioli ùn hè micca cercata".

Fighjemu a classa HelloService - ùn hà veramente micca una tale annotazione. Per quessa, deve esse aghjuntu per chì Quarkus pò circà è truvà u fasgiolu. E postu chì questu hè un oggettu senza statu, pudemu aghjunghje facilmente l'annotazione @ApplicationScoped cum'è questu:

@ApplicationScoped
public class HelloService {

Nutate bè: quì l'ambiente di sviluppu pò dumandà à aghjunghje u pacchettu necessariu (vede a linea sottu), è avete da fà questu manualmente, cusì:

import javax.enterprise.context.ApplicationScoped;

Sè avete dubbitu nantu à quale scopu deve esse usatu in u casu quandu ùn hè micca specificatu per u bean fonte, leghjite a documentazione. JSR 365: Cuntesti è Iniezione di Dipendenza per Java 2.0 - Scopu predefinitu.

Avà pruvemu di novu à lancià l'applicazione cù u cumandimu ./mvnw compile 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]

Avà tuttu va senza errore.

Lanciamentu di u helloworld mudernizatu
Comu scrittu in u logu, apre in u navigatore 0.0.0.0:8080 (a pagina iniziale di Quarkus predeterminata) è vedemu questu:

Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Risu. 4. Quarkus dev pagina iniziale.

L'annotazione WebServlet per questa applicazione cuntene a seguente definizione di cuntestu:

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

Dunque, andemu in u navigatore 0.0.0.0:8080/HelloWorld è vedemu i seguenti:

Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Risu. 5: A pagina di sviluppu di Quarkus per l'applicazione Hello World.

Ebbè, tuttu funziona.

Avà andemu à fà cambiamenti à u codice. Nota chì u cumandamentu ./mvnw compile quarkus:dev hè sempre in esecuzione è ùn avemu micca intenzione di piantà. Avà pruvemu à applicà i stessi cambiamenti - assai triviali - à u codice stessu è vede cumu Quarkus rende a vita più faciule per u sviluppatore:

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

Salvà u schedariu è poi rinfriscà a pagina web per vede Hello Marco, cum'è mostra in a screenshot sottu:

Quarkus: Modernizazione di l'Applicazione Utilizendu Helloworld cum'è Esempiu da JBoss EAP Quickstart

Risu. 6. Hello Marco pagina in Quarkus dev.

Avà cuntrollamu l'output in u terminal:

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

U rinfrescante di a pagina hà attivatu a rilevazione di cambiamenti in u codice fonte, è Quarkus hà realizatu automaticamente una prucedura di stop-start. È tuttu questu hè statu cumpletu in solu 0.371 seconde (eccu quì, quellu "Ultra-fast subatomic Java").

Custruisce helloworld in un pacchettu JAR
Avà chì u codice funziona cum'è duverebbe, imballemu cù u cumandimu seguente:

$ ./mvnw clean package

Stu cumanda crea dui schedari JAR in u cartulare /target: u schedariu helloworld-.jar, chì hè un artefattu standard assemblatu da a squadra Maven cù e classi è risorse di u prugettu. È u schedariu helloworld-runner.jar, chì hè un JAR eseguibile.

Per piacè nutate chì questu ùn hè micca un uber-jar, postu chì tutte e dipendenze sò simpliciamente copiate in u cartulare /target/lib (micca imballatu in un schedariu JAR). Dunque, per eseguisce stu JAR da un altru cartulare o in un altru òspite, avete bisognu di copià u schedariu JAR stessu è u cartulare / lib quì, postu chì l'elementu Class-Path in u schedariu MANIFEST.MF in u pacchettu JAR cuntene. una lista esplicita di JAR da i cartulare lib
Per amparà cumu creà applicazioni uber-jar, per piacè riferite à u tutoriale Uber-Jar Creazione.

Lanciate helloworld imballatu in JAR

Avà pudemu eseguisce u nostru JAR utilizendu u cumandimu standard 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]

Dopu tuttu questu hè fattu, andate à u vostru navigatore à 0.0.0.0:8080 è verificate chì tuttu funziona cum'è deve.

Cumpilà helloworld in un schedariu eseguibile nativu

Allora u nostru helloworld funziona cum'è una applicazione Java autonoma cù dipendenze di Quarkus. Ma pudete andà più luntanu è trasfurmà in un schedariu eseguibile nativu.

Installazione di GraalVM
Prima di tuttu, per questu avete bisognu di stallà i strumenti necessarii:

1. Download GraalVM 19.2.0.1 da github.com/oracle/graal/releases/tag/vm-19.2.0.1.

2. Espandi l'archiviu telecaricatu:

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

3. Andà à u cartulare untar.

4. Eseguite u cumandimu sottu per scaricà è aghjunghje l'imaghjini nativu:

$ ./bin/gu install native-image

5. Registrate u cartulare creatu in u passu 2 à a variabile d'ambiente GRAALVM_HOME:

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

Per più infurmazione è struzzioni di installazione nantu à altri OS, fate cunsultà u manuale. Custruì un Nativu Executable-Prerequisites.

Custruisce helloworld in un schedariu eseguibile nativu
Leghjendu u manuale Custruisce un eseguibile nativu - Pruduzzione di un eseguibile nativu: "Avà criemu un schedariu eseguibile nativu per a nostra applicazione per riduce u so tempu di lanciamentu è a dimensione di u discu. U schedariu eseguibile avarà tuttu ciò chì hè necessariu per eseguisce l'applicazione, cumpresa a JVM (o megliu, una versione truncata di questu, chì cuntene solu ciò chì hè necessariu per eseguisce l'applicazione) è a nostra applicazione stessa ".

Per creà un schedariu eseguibile nativu, avete bisognu di attivà u prufilu nativu Maven:

$ ./mvnw package -Pnative

A nostra custruzzione hà pigliatu un minutu è 10 seconde, è u schedariu finale helloworld-runner f hè statu creatu in u cartulare /target.

Eseguite l'eseguibile nativu helloworld

In u passu precedente, avemu ricevutu u schedariu eseguibile /target/helloworld-runner. Avà eseguimu:

$ ./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]

Apertura di novu in u navigatore 0.0.0.0:8080 è verificate chì tuttu funziona cum'è deve.

À seguità!

Cridemu chì u metudu di mudernizà l'applicazioni Java utilizendu e capacità di Quarkus discutitu in questu post (ancu cù un esempiu simplice) deve esse attivamente utilizatu in a vita reale. Fendu cusì, vi prubabilmente scontru una quantità di prublemi, chì avemu da parzialmente affruntà in u prossimu post, induve parleremu di cumu si misurà u cunsumu di memoria per valutà i migliori di u rendiment, una parte impurtante di tuttu u prucessu di mudernizazione di l'applicazione.

Source: www.habr.com

Cumprate un hosting affidabile per i siti cù prutezzione DDoS, servitori VPS VDS 🔥 Cumprate un hosting di siti web affidabile cù prutezzione DDoS, servitori VPS VDS | ProHoster