Wydajność Twojej aplikacji nie zależy wyłącznie od kodu – kluczowy jest sposób, w jaki kontener webowy zarządza żądaniami i wątkami.
Przybliżę Ci różnice między Tomcatem, Jetty i Netty – trzema popularnymi serwerami, które znacząco różnią się podejściem do obsługi ruchu.
🔍Tomcat – klasyczny model blokujący
Tomcat to najbardziej rozpowszechniony serwer aplikacyjny w ekosystemie Java.
Domyślnie: Tomcat konfiguruje pulę 200 wątków roboczych (maxThreads=200
). Każde przychodzące żądanie HTTP przypisuje do wolnego wątku.
Parametry zarządzania wątkami:
- maxThreads – maksymalna liczba wątków, które mogą równocześnie obsługiwać przychodzące żądania HTTP. Gdy liczba żądań przekracza tę wartość, kolejne trafiają do kolejki oczekujących.
🔹 Domyślnie: 200. - minSpareThreads – minimalna liczba wątków utrzymywanych w stanie gotowości, nawet gdy są bezczynne. Dzięki temu serwer może szybko zareagować na nowe żądania bez konieczności tworzenia wątków „na żądanie”. Pozostałe wątki pozostają w puli w stanie bezczynności (aby nie zużywać niepotrzebnie zasobów), gotowe do ponownego użycia.
🔹 Domyślnie: 10. - acceptCount – maksymalna liczba żądań, które mogą oczekiwać w kolejce, gdy wszystkie
maxThreads
są zajęte. Jeśli limit ten zostanie przekroczony (np. 200 aktywnych wątków + 100 w kolejce), każde kolejne żądanie (np. 301.) zostaje odrzucone, co skutkuje błędem lub timeoutem.
🔹 Domyślnie: 100.
Jak działa request flow:
- Klient wysyła request (np.
GET /users/1
). - Gniazdo (socket) akceptowane jest przez AcceptorThread.
- Trafia do Executor, który pobiera wątek z puli.
- Wątek obsługuje request: np. odczytuje dane z DB, renderuje widok.
- Odpowiedź wysyłana, wątek wraca do puli.

🔴 Jeśli wszystkie 200 wątków są zajęte – kolejne requesty czekają w kolejce (acceptCount=100
), a potem… są odrzucane.
🌀 Jetty – lekki i elastyczny serwer
Jetty również operuje na modelu wątków per request, ale z bardziej elastyczną konfiguracją i mniejszym narzutem. Jest częściej używany tam, gdzie liczy się niska latencja i możliwość dostosowania.
Jetty pozwala też na konfigurację hybrydową – częściowo nieblokującą, dzięki adnotacji @WebServlet(asyncSupported = true)
oraz metodzie startAsync()
.
Nie jest to jednak pełnoprawny reactive stack – nadal sporo zależy od architektury Twojej aplikacji.
Jak działa request flow w Jetty (blokująco):
- Request trafia do
QueuedThreadPool
. - Wątek obsługuje całość, analogicznie do Tomcat.
- Możliwość ręcznego „zawieszenia” przetwarzania i zwolnienia wątku (
startAsync()
).
Z asynchronicznym API:
- Wątek może wrócić do puli zanim response zostanie wygenerowany.
- Odpowiedź może być przekazana np. przez callback lub event.
✅ Jetty lepiej znosi bursty i scenariusze WebSocketowe, choć nadal to nie w pełni reaktywny model.
⚡Netty – w pełni asynchroniczny, event-driven
Netty działa na zupełnie innym poziomie. Netty nie oferuje gotowego serwera HTTP jak Tomcat czy Jetty – zamiast tego udostępnia niskopoziomowy model, który pozwala zbudować serwer HTTP z pełną kontrolą nad ruchem i zdarzeniami sieciowymi.
Netty działa w modelu Event Loop. Domyślnie ma:
- 1–2 wątki akceptujące – boss group (domyślnie 1),
- liczba rdzeni CPU × 2 wątków przetwarzających (worker group).
Jak dział request flow:
- Połączenie akceptowane przez
bossGroup
. - Trafia do
workerGroup
, gdzie zarządzane jest przez selektory. - Wszystko przetwarzane asynchronicznie, przez zdarzenia (
channelRead
,write
,flush
, itd.) a dane przekazywane są w postaci obiektów Netty (np.ByteBuf
,FullHttpRequest
). - Callbacki reagują na zakończenie operacji (np. dostęp do bazy przez
CompletableFuture
lubMono
).

✅ Jeden wątek Netty może obsłużyć tysiące połączeń, bo nie blokuje IO.
📊 Porównanie: obsługa wątków i przetwarzanie

Cecha | Tomcat | Jetty | Netty |
---|---|---|---|
Model | Blokujący | Blokujący / Hybrydowy | W pełni nieblokujący |
Wątek per request | Tak | Tak (z opcją async) | Nie – Event Loop |
Domyślna pula wątków | 200 (maxThreads ) | 250 (QueuedThreadPool ) | 2 × liczba dostępnych rdzeni CPU + 1 bossGroup |
Skalowalność | Ograniczona pulą wątków | Średnia, ale lepsza od Tomcat | Wysoka |
Konfiguracja | Prosta | Elastyczna | Złożona, niskopoziomowa (Spring Boot zapewnia konfiguracje za nas) |
Idealny use case | Klasyczne aplikacje webowe | Lightweight REST, WebSocket | Reactive, event-driven apps |
🎯Kiedy wybrać który?
Tomcat i Jetty to świetne rozwiązania dla klasycznych aplikacji – łatwe w konfiguracji, sprawdzone. Ale w sytuacji, gdy liczba requestów rośnie wykładniczo, wątek per request przestaje być opłacalny.
Netty oferuje zupełnie inny poziom: w pełni reaktywna obsługa, która minimalizuje zużycie zasobów. Wymaga jednak więcej wysiłku – zarówno od strony konfiguracyjnej jak i zasad implementacji rozwiązania, które na nim działa.
🔹 Tomcat – dla większości klasycznych aplikacji Spring MVC, kiedy nie przewidujesz gigantycznego ruchu.
🔹 Jetty – gdy chcesz więcej kontroli i elastyczności przy mniejszym narzucie, np. w microservices.
🔹 Netty – jeśli tworzysz system wymagający maksymalnej wydajności i kontroli nad ruchem – API gateway, serwer HTTP/2, websockety, broker wiadomości.
Co z wirtualnymi wątkami?
Od JDK 21 wchodzą w grę wirtualne wątki (Project Loom) – przełom, który może zmienić podejście do serwerów opartych na wątkach per request. Umożliwiają one obsługę milionów requestów bez zmiany architektury na reaktywną.
Ważne – wirtualne wątki zmieniają model wykonania, ale nie rozwiązują wszystkiego – nadal wymagają przemyślanej współpracy z blokującymi zasobami (np. I/O, bazy danych).
🎬 Zobacz, jak wirtualne wątki przyspieszają aplikacje – pełne szkolenie!
Sprawdź mój materiał na YouTube, w którym krok po kroku pokazuję, jak uruchomić wirtualne wątki w Spring Boot. Live coding, praktyczne przykłady i porównanie wydajności – wszystko, czego potrzebujesz, aby wynieść swoje API na wyższy poziom!