Previous slide Next slide Toggle fullscreen Open presenter view
Quarkus Reactive in Action
Stefan Trenkel, Matthias Bremer | team neusta
Wir
Stefan Trenkel, Matthias Bremer | team neusta
Historie
11/2019 1.0 Red Hat
6/2021 2.0
4/2023 3.0 Jakarta 10, MicroProfile 6, Hibernate 6
7/2023 3.2 LTS
2/2024 3.8 LTS
4/2024 3.10
7/2024 Commonhaus Foundation
9/2024 3.15 LTS
CDI (ArC)
implementiert CDI Lite specification
Reflections vermeiden (teuer)
Non-standard Features:
Removing Unused Beans
Lazy Bean Creation
Qualified Injected Fields
siehe: https://quarkus.io/guides/cdi-reference#nonstandard_features
Dev Mode
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2024-09-15 12:54:51,134 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2024-09-15 12:54:51,135 INFO [io.quarkus] (Quarkus Main Thread) Installed features:
[agroal, cdi, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, narayana-jta, resteasy, resteasy-jsonb, smallrye-context-propagation, vertx]
--
Tests paused
Press [e] to edit command line args (currently ''), [r] to resume testing, [o] Toggle test output, [:] for the terminal, [h] for more options>
Live Reload
Continuous Testing
Dev Services - zero Configuration
Hibernate Panache (Repository-Pattern)
@Entity
public class Cattle { ...}
@ApplicationScoped
public class CattleRepository implements PanacheRepository <Cattle> { ... }
@Inject
CattleRepository cattleRepository;
cattleRepository.persist(cattle);
long count = cattleRepository.count("birthday" , LocalDate.of(1940 , 3 , 10 ));
Hibernate Panache (Active-Record Pattern)
@Entity
public class Cattle extends PanacheEntity { ...}
cattle.persist();
long count = Cattle.count("birthday" , LocalDate.of(1940 , 3 , 10 ));
Quarkus Test
@QuarkusTest
@TestHTTPEndpoint(CattleResource.class)
class CattleResourceTest {
@Test
void testGeCattle () {
given().pathParam("lom" , 276000400000001L )
.when().get("{lom}" )
.then().log().ifValidationFails()
.statusCode(200 )
.body(is("{\"birthday\":\"1940-03-10\",\"lom\":276000400000001}" ));
}
Reactive
Was ist Reactive?
Reactive is a set of principles to build robust , efficient , and concurrent applications and systems.
Was bedeutet das?
These principles let you handle more load than traditional approaches while using the resources (CPU and memory) more efficiently while also reacting to failures gracefully.
Quelle: https://quarkus.io/guides/getting-started-reactive
Imperativ
jeder Request bekommt einen Worker-Thread aus dem Threadpool
der Thread ist blockiert für die Zeit des Requests
ein Request entspricht damit genau einem Thread
Code ist straight forward
Quelle: Davi Vieira - Designing Hexagonal Architecture with Java
Reactive
ein Thread kann mehrere Requests bearbeiten
non-blocking I/O-Threads
DB-Operationen etc. blockieren die Threads nicht
arbeitet mit Callbacks
während eine I/O-Option noch läuft, kann der Thread schon den nächsten Request annehmen
Quelle: Davi Vieira - Designing Hexagonal Architecture with Java
Reactive und Quarkus
innere Quarkus-Architektur
intern non-Blocking I/O-Threads
Eclipse Vert.x, Netty
als User: reactive Programming mit Mutiny
@Blocking erlaubt imperativen Code
Quelle: https://quarkus.io/guides/quarkus-reactive-architecture
(Klassische) Jakarta-Implementierung
@Inject
CattleRepository cattleRepository;
CattleDto findCattle (Long lom) {
Cattle cattle;
try {
cattle = cattleRepository.find("lom" , lom).singleResult();
} catch (NoResultException e) {
return null ;
}
return CattleDto.from(cattle);
}
Reactive mit SmallRye Mutiny
@Inject
CattleRepository cattleRepository;
@WithTransaction
Uni<CattleDto> findCattle (Long lom) {
return cattleRepository.find("lom" , lom).singleResult()
.onItem().transform(CattleDto::from)
.onFailure(NoResultException.class).recoverWithNull();
}
Wildfly
Quarkus imperativ
Quarkus reactive
Umgebung
Lokal auf Notebook: 8 CPUs, i7 von 2018
Ubuntu (Performance Setting)
PostgreSQL
Docker-Container (CPU und RAM begrenzt)
Quarkus 3.8 LTS + Wildfly 31
Startup
Quarkus imperativ:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
ee5217f76198 cattle-quarkus-classic-quarkus-app-1 0.34% 130.8MiB / 512MiB 25.54% 9.91kB / 12kB 0B / 119kB 23
Startup: 6.0 s
Quarkus reactive:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
1a346ce10311 cattle-quarkus-reactive-quarkus-app-1 0.27% 136.8MiB / 512MiB 26.72% 173kB / 8.01kB 0B / 111kB 21
Startup: 6.2 s
Startup
Wildfly:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
374a381d75b6 cattle-wildfly-wildfly-1 0.06% 504.6MiB / 512MiB 98.55% 19.5kB / 19.5kB 471MB / 778MB 54
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
7d54f15b952e cattle-wildfly-wildfly-1 0.05% 557.7MiB / 1GiB 54.46% 19.4kB / 19.5kB 10.9MB / 2.79MB 54
+ Startup ca. 30 s
POST 1000 Tiere
/
curl -X POST ... &
curl -X POST ...
Quarkus imperativ
12
14
Quarkus reactive
11
14
Wildfly
16
17
GET
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
1000 VUs
1 Request pro Sekunde
Random Tier 1..1000
20 Iterationen
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: k6/script.qclassic.js
output: -
scenarios: (100.00%) 1 scenario, 1000 max VUs, 10m30s max duration (incl. graceful stop):
* default: 20000 iterations shared among 1000 VUs (maxDuration: 10m0s, gracefulStop: 30s)
data_received..................: 2.1 MB 45 kB/s
data_sent......................: 1.8 MB 39 kB/s
http_req_blocked...............: avg=3ms min=1.37µs med=3.46µs max=235.01ms p(90)=6.01µs p(95)=487.95µs
http_req_connecting............: avg=2.9ms min=0s med=0s max=234.95ms p(90)=0s p(95)=94.45µs
http_req_duration..............: avg=1.29s min=1.56ms med=590.48ms max=10.45s p(90)=3.19s p(95)=4.82s
{ expected_response:true }...: avg=1.29s min=1.56ms med=590.48ms max=10.45s p(90)=3.19s p(95)=4.82s
http_req_failed................: 0.00% ✓ 0 ✗ 20000
http_req_receiving.............: avg=52.19µs min=14.46µs med=36.75µs max=10.79ms p(90)=66.06µs p(95)=84.88µs
http_req_sending...............: avg=702.66µs min=5.36µs med=15.07µs max=200.94ms p(90)=172.89µs p(95)=812.23µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=1.29s min=1.48ms med=590.39ms max=10.35s p(90)=3.19s p(95)=4.82s
http_reqs......................: 20000 429.218332/s
iteration_duration.............: avg=2.29s min=1s med=1.59s max=11.55s p(90)=4.19s p(95)=5.82s
iterations.....................: 20000 429.218332/s
vus............................: 752 min=752 max=1000
vus_max........................: 1000 min=1000 max=1000
running (00m46.6s), 0000/1000 VUs, 20000 complete and 0 interrupted iterations
default ✓ [======================================] 1000 VUs 00m46.6s/10m0s 20000/20000 shared iters
10 VUs - 1 Request pro Sekunde
Quarkus imperativ:
http_req_duration..............: avg=15.08ms min=3.45ms med=5.4ms max=206.75ms p(90)=13.64ms p(95)=97.5ms
Quarkus reactive:
http_req_duration..............: avg=10.85ms min=2.61ms med=5.14ms max=169.34ms p(90)=10.68ms p(95)=46.89ms
Wildfly:
http_req_duration..............: avg=7.22ms min=4.08ms med=6.72ms max=20.25ms p(90)=10.47ms p(95)=11.92ms
1000 VUs - 1 Request pro Sekunde
Quarkus imperativ:
http_req_duration..............: avg=905.76ms min=1.64ms med=392.46ms max=5.25s p(90)=2.2s p(95)=2.88s
Quarkus reactive:
http_req_duration..............: avg=1.05s min=205ms med=723.27ms max=6.65s p(90)=2.09s p(95)=3.12s
Wildfly:
http_req_duration..............: avg=2.87s min=1.2s med=2.41s max=7.77s p(90)=4.4s p(95)=4.8s
1000 VUs - 1 Request pro Sekunde - nach 5 Iterationen
Quarkus imperativ:
http_req_duration..............: avg=30.64ms min=594.27µs med=2.35ms max=590.12ms p(90)=44.5ms p(95)=168.4ms
Quarkus reactive:
http_req_duration..............: avg=41.78ms min=874.91µs med=7.39ms max=850.99ms p(90)=67.72ms p(95)=93.8ms
Wildfly:
http_req_duration..............: avg=1.08s min=45.08ms med=1.09s max=2.1s p(90)=1.2s p(95)=1.28s
2000 VUs - 1 Request pro Sekunde
Quarkus imperativ:
http_req_duration..............: avg=2.43s min=14.37ms med=1.5s max=12.71s p(90)=5.18s p(95)=8.89s
Quarkus reactive:
http_req_duration..............: avg=2.78s min=102.01ms med=2.19s max=13.64s p(90)=4.42s p(95)=7.07s
Wildfly:
http_req_duration..............: avg=6.58s min=75.54ms med=5.98s max=17.65s p(90)=9.51s p(95)=12.28s
Vorläufiges Fazit - Quarkus Reactive
Schön, dass unser Thema euer Interesse geweckt hat.
Matthias "Quarkus"
Stefan "Reactive"
in Action ???
Hat jemand Erfahrungen mit Quarkus?
Mit Quarkus und Reactive?
seit xxx Software-Entwickler bei neusta SD.
Seit xxx Jahren beim gleichen Kunden...
in unterschiedlichen Projekten
Schwerpunkt Java / JakartaEE,
früher auf Tomcat und Wildfly
jetzt immer mehr Quarkus
1. Kunde traditionell - setzt auf Standards
2. Backendlastig
3. Quarkus-Standards: CDI/ArC, Hibernate, RestEasy, JsonB, Microprofile SmallRye
4. leichtgewichtiger: leichtes Setup, schnelles Startup, QuarkusTest, Dev-Mode
5. Webservices, CLI- bzw. Batch-Programme
6. seit ca. 1,5 Jahren Docker-Swarm und Quarkus = Standard fürs Backend
1. Red Hats Antwort auf Spring Boot (lt. Wikipedia 10 Jahre alt)
2. Red Hat hatte schon erfahrungen mit JBoss bzw. Wildfly
3. rasante Entwicklung (für ein Java-Framework)
4. nächste 3.15 LTS am 25.9.2024
5. 12 Monate Support für LTS-Versionen
0. Optimierung für HotSpot-Compiler und GraalVM
1. Annotation-Scanning und Optimierung zur Compilezeit
2. Reflections ist damit nur eingeschränkt erlaubt
3. Ungenutzte Klassen werden zur Buildzeit entfernt werden
4. Beans werden erzeugt, wenn sie gebraucht werden (z.B. RequestScoped)
5. Inject-Annotation kann entfallen, Bsp. RestClient, ConfigProperty
einfach im Vergleich zu klassischem Jakarta-Appserver
erstmal schauen, was das Gegenteil von Reactive eigentlich ist
* Eclipse Vert.x: asynchrones Programmier-Framework
* Netty: asynchrones Netzwerk-Framework
* @NonBlocking ist ein Hint an das Backend
* mit Panache-Repository
* Wildfly so ähnlich
Quarkus ist im Backend immer reactive
Quarkus und Wildfly mit gleichem Stand Hibernate
Startup - docker compose mit Postgres
1 GET Request
* vorher 1 GET
* leere DB
* auf Sekunde gerundet
* Simples Scenario
* Skript dazu ist im Repo
* Performance-Messung ist schwierig
* HotSpot-Compiler (JIT) + Caching
* Was man bekommt sieht etwa so aus
* http_req_duration
* median und 90. oder 95. Perzentil
* mit warmem Compiler / Cache
* im Vergleich mit klassischem Jakarta App-Server
* Quarkus macht auch noch Spaß
* reactive ist wahrscheinlich ein Grund für die gute Quarkus-Performance