Einleitung
Es gibt einen hartnäckigen Glauben in der Java-Community, dass com.sun.net.httpserver.HttpServer eine Art “internes JDK-Ding” ist, das man nicht anfassen sollte.
Dieser Glaube ist falsch.
Ja, der Paketname beginnt mit com.sun.*, was aussieht, als gehöre es in die Kategorie “Finger weg” – aber com.sun.net.httpserver.HttpServer ist nicht dieselbe Kategorie wie sun.*-Interna. Es ist Teil jeder zertifizierten JRE, es ist dokumentiert und es ist nicht als “experimentell” oder “nicht für den Produktionseinsatz” gekennzeichnet. Mit anderen Worten: Wir können es einfach verwenden. Cool!
Das wirft eine praktische Frage auf:
Brauchen wir immer Jetty, Netty, Undertow usw. nur um HTTP zu bedienen? Und wenn wir einen Drittanbieter-HTTP-Server durch den ersetzen, der mit dem JDK kommt, was gewinnen (und verlieren) wir?
Ein offensichtlicher Vorteil ist der langweilige, aber wichtige: weniger Abhängigkeiten. Weniger Abhängigkeiten bedeuten generell:
- kleinere Binaries/Images,
- einfachere Upgrades und weniger CVE-Feuerwehreinsätze
- eine kleinere Angriffsfläche (besonders hinsichtlich Supply-Chain-Risiko),
- weniger Code, den Sie nicht geschrieben haben und nicht überprüfen und verstehen müssen
Vor einiger Zeit habe ich etwas Dummes, aber Lustiges gemacht und Ninja – ein Java-Web-Framework aus den 2010ern – in modernes Java umgeschrieben unter Verwendung von KI und den neuesten Errungenschaften der Java-Welt, und es NinjaX genannt.
Und weil es so viel Spaß gemacht hat, hatte ich eine weitere Idee: Warum nicht Jetty (den aktuellen Standard-Webserver in NinjaX) durch den eingebauten HttpServer des JDK ersetzen? Dieser Beitrag handelt genau davon, komplett mit Performance-Tests und Links, die zeigen, wie es aussieht.
KI für die Konvertierung verwenden
Der spaßige Teil: Ich hatte bereits eine funktionierende Jetty-basierte Implementierung. Der nervige Teil: Jetty gibt Ihnen vieles “gratis” (Request-Parsing, Cookies, Multipart-Formularhandling usw.), während Javas HttpServer absichtlich minimal ist.
Genau da hat KI geholfen.
Ich habe ChatBox verwendet (keinen vollständigen Coding-Agenten), und einen kleinen Hin-und-Her-Dialog rund um meine NinjaJetty-Klasse. Der Prompt war im Wesentlichen:
“Can you rewrite this file to not use Jetty, but only use
com.sun.net.httpserver.HttpServer; (with executorserver.setExecutor(Executors.newVirtualThreadPerTaskExecutor());)”
Und… es hat überraschend gut funktioniert. Nach etwas Hin und Her mit der KI hat sie sogar den nervigsten Teil bewältigt: Multipart-Parsing (einschließlich Zeilenumbruch-Randfälle).
Der einzige manuelle Hinweis, den ich der KI gab, war das Hinzufügen eines performanteren Executors:
httpServer.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
Einige Anmerkungen aus dieser Übung:
- Javas
HttpServerist absichtlich sehr minimal. Wenn Sie Cookie-Parsing, Multipart-Parsing und andere HTTP-Komfortfunktionen möchten, implementieren Sie sie selbst (oder lassen Sie die KI einen Entwurf erstellen und testen/verifizieren dann). - Ich habe später Tests hinzugefügt – sehr empfehlenswert, da Request-Parsing-Bugs die Art sind, die gut aussehen, bis sie Ihnen in der Produktion den Tag ruinieren.
- Ohne KI hätte ich es wahrscheinlich nicht so schnell geschafft. Besonders Multipart-Parsing: Ich schätze 1-2 Tage Arbeit für eine sorgfältige, korrekte Implementierung.
Benchmark-Ergebnisse (nicht wissenschaftlich, aber dennoch nützlich)
Seien wir ehrlich: Mikro-Benchmarks sind leicht zu verfälschen – oft unbeabsichtigt. Dies ist kein produktionsnaher Benchmark.
Aber er kann trotzdem eine grobe Frage beantworten: Befindet sich Javas eingebauter HttpServer in derselben Performance-Klasse wie Jetty für einen trivialen Endpunkt?
Zu diesem Zweck habe ich ein kleines Repository mit dem gesamten für Tests benötigten Code erstellt.
Setup
- Maschine: Mac M1 Max, 32 GB RAM
- Tool:
bombardier - Befehl:
bombardier -c 100 -n 5000000 http://localhost:8080/health
- Endpunkt:
/healthgibt einen einfachen String zurück (NinjaX, kein JSON, keine DB, kein echter Payload)
HttpServer (Baseline - mit Standard-Executor)
Dies sollte langsam sein, da der Standard-Executor alles in einem Thread ausführt. Aber es ist nützlich, eine Baseline zu etablieren und zu sehen, ob andere Executors die Performance verbessern.
Abhängigkeit:
<dependency>
<groupId>org.r10r</groupId>
<artifactId>ninjax-core</artifactId>
<version>${ninja.version}</version>
</dependency>
Ergebnisse:
Statistics Avg Stdev Max
Reqs/sec 47132.27 4668.50 61810.37
Latency 2.12ms 1.07ms 305.37ms
HTTP codes:
1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0
others - 0
Throughput: 8.85MB/s
Anmerkung zur Binärgröße: ~3,9 MB (hauptsächlich NinjaX, kein extra JSON/DB/etc).
HttpServer (mit newVirtualThreadPerTaskExecutor)
Gleiche Abhängigkeit wie oben, aber mit:
httpServer.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
Ergebnisse:
Statistics Avg Stdev Max
Reqs/sec 82252.91 14664.57 152264.38
Latency 1.21ms 469.38us 127.44ms
HTTP codes:
1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0
others - 0
Throughput: 15.45MB/s
Anmerkung zur Binärgröße: immer noch ~3,9 MB.
Doppelt so schnell mit nur einer geänderten Zeile im Vergleich zum Standard-Executor. Schön!
Jetty
Abhängigkeit:
<dependency>
<groupId>org.r10r</groupId>
<artifactId>ninjax-jetty</artifactId>
<version>${ninja.version}</version>
</dependency>
Ergebnisse:
Statistics Avg Stdev Max
Reqs/sec 79878.89 9117.21 129107.62
Latency 1.25ms 1.47ms 362.39ms
HTTP codes:
1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0
others - 0
Throughput: 15.34MB/s
Anmerkung zur Binärgröße: ~7,1 MB.
Interessant – es ist sogar etwas langsamer als der HttpServer mit einem geeigneten Executor.
Diskussion: Was das beweist (und was nicht)
Dieser Benchmark ist absichtlich einfach, weshalb er auch genau deshalb nicht repräsentativ ist:
- Payload und Response sind winzig.
- Es gibt keine JSON-Serialisierung, keine Templates, keine DB-Aufrufe, kein TLS, keine Proxy-Header, keine Auth, keine echte Routing-Komplexität.
- Jetty wurde nicht optimiert oder “gepusht”; es ging sogar durch Servlet-Mapping, was nicht der schlankeste Weg ist.
newVirtualThreadPerTaskExecutor()mag fürHttpServernicht bei jedem Workload ideal sein. (Virtual Threads glänzen, wenn Sie blockieren; sie sind kein Zaubermittel für jeden CPU-gebundenen Fall.)
Und zur Größendifferenz: Ja, der Wechsel von Jetty zu purem JDK hat den Footprint reduziert (7,1 MB → 3,9 MB). Aber es ist leicht, diese Zahl überzuinterpretieren. Echte Services ziehen normalerweise DB-Treiber, Observability, Config, Auth, JSON usw. heran. Sobald Sie diese hinzufügen, schrumpft der relative Prozentsatz.
Dennoch ergeben sich einige nützliche Erkenntnisse:
- Die Performance ist in derselben Klasse. Für einen trivialen Endpunkt kann der JDK
HttpServerplus Virtual Threads ein grundlegendes Jetty-Setup in rohen Req/s erreichen (oder leicht übertreffen). - Sie können Abhängigkeiten sinnvoll reduzieren, wenn Ihre Anforderungen einfach sind und Sie bereit sind, einige fehlende HTTP-Komfortfunktionen zu implementieren.
- Für bestimmte Klassen von Services – interne Tools, leichtgewichtige APIs, eingebettete Admin-Endpunkte, Sidecars, Test-Fixtures – kann das ein sehr attraktiver Tausch sein.
Zusammenfassung
Wenn Sie einen kleinen, abhängigkeitsarmen Java-HTTP-Server möchten, übersehen Sie nicht, was das JDK bereits mitliefert.
com.sun.net.httpserver.HttpServerist minimal, aber nutzbar.- Mit einem Virtual-Thread-Executor kann er in einfachen Szenarien einen Durchsatz liefern, der mit Jetty vergleichbar ist.
- Der große Trade-off ist Komfort: Sie müssen Dinge implementieren (oder einbinden), die Jetty normalerweise bietet – Cookies, Multipart-Parsing und andere HTTP-Ergonomie.
- KI ist ein überraschend effektiver Beschleuniger für diese Art von “Portierungsarbeit”, besonders wenn Sie bereits eine funktionierende Referenzimplementierung haben und mit Tests validieren.
Wenn Sie auf weniger Abhängigkeiten und einen kleineren Supply-Chain-Footprint optimieren, kann Javas eingebauter HttpServer eine praktische Option sein – nicht nur eine Kuriosität.
Mehr
- Performance + Virtual Threads Write-up: https://medium.com/deno-the-complete-reference/performance-benefits-of-virtual-threads-hello-world-http-server-6c32efe9f986
- NinjaX: https://github.com/raphaelbauer/ninjax
