Die verbreitetste Implementierung von Microservices, Spring Boot in der Java Virtual Machine (JVM), ist unumstritten. Doch was hat es mit dem Trend, Java-Applikationen als Microservices umzusetzen, auf sich? Dieser Beitrag zeigt das auf und vergleicht JVM mit plausiblen Alternativen. Der Fokus liegt hier auf Pistache.
Pistache ist ein in modernem C++ geschriebenes Web-Framework, das insbesondere auf Performance ausgelegt ist.
Der in den vergangenen Jahren entstandene Trend, Java-Applikationen für das Internet als Microservices umzusetzen oder bestehende Monolithen in solche zu migrieren, wird in letzter Zeit immer häufiger hinterfragt. Dafür gibt es eine Vielzahl von Gründen.
Hierzu zählen beispielsweise die in Anspruch genommenen Hardware-Ressourcen oder die resultierende Performance. Interessant sind nebst den Antwortzeiten auch die Startzeit der Applikation und deren Ressourcenverbrauch. Dabei liegt der Fokus auf dem Betrieb orchestrierter Container in einer Cloud.
Die Kommodität für den Entwickler bei der Erstellung der Software sowie die damit verbundene „Time to Market“-Geschwindigkeit stehen dem ökologischen und ökonomischen Fußabdruck des Betriebs gegenüber. Nicht zu unterschätzen ist, dass auch bei der Software-Entwicklung ein Beitrag zum nachhaltigen Umgang mit der Natur und ihren Ressourcen geleistet werden kann.
Ursprung von Java, Portabilität heute und State of the Art
Die Programmiersprache Java wurde einst mit dem Fokus auf Portabilität entwickelt. Der Marketing-Slogan „Write once, run anywhere“ von Sun Microsystems aus dem Jahre 1995 verdeutlicht dies. Diese Portabilität ist in der Cloud nicht mehr notwendig, da der Container das Betriebssystem und die Prozessorarchitektur vorgibt.
Die Kompilierung zu portablem ByteCode für die JVM erzeugt einen unnötig gewordenen Mehraufwand. Mittels GraalVM können native Binaries erzeugt werden, was dieses Problem zur Laufzeit entschärft. Was noch übrig bleibt, sind die langen Build-Zeiten und die Auswirkungen der Garbage Collection. Diese letzte Hürde lässt sich nicht so leicht lösen, da das Konzept der automatischen Speicherverwaltung stark mit der Sprache verbunden ist. Die meisten Java-Entwickler würden darauf aber kaum verzichten wollen.
Will man den Optimierungsgedanken jedoch weiterverfolgen, bietet sich eine Implementierung der Anwendung in einer Sprache wie C++ an. Die Garbage Collection und die Virtual Machine entfallen hier komplett. Die Verantwortung der Speicherverwaltung liegt dann beim Entwickler. Dank einer modernen Herangehensweise und den Möglichkeiten, welche aktuelle C++ Standards bieten, ist dies jedoch unproblematisch.
Und jetzt? Verschiedene Möglichkeiten im Vergleich
Es gibt zahlreiche, vielversprechende Bibliotheken, die in Frage kommen und getestet wurden. Das C++ REST SDK von Microsoft schien zunächst eine interessante Option zu sein, da die Bibliothek unter der MIT-Lizenz herausgegeben wird und in vielen Linux-Distributionen über zahlreiche, offzielle Linux-Repositories bezogen werden kann.
Wie sich jedoch herausgestellt hat, weist die Bibliothek eine teils problematische Performance bei der JSON-Generierung auf. Es existieren dazu unabhängige Benchmarks, welche dies deutlich aufzeigen. Der Benchmark zeigt, dass im Vergleich zwischen C++ REST SDK, restbed und Pistache, letztere Bibliothek die besten Ergebnisse aufweist. Andere, hier nicht geprüfte Alternativen wären Beast, Drogon, oat++ oder das teils kostenpflichtige POCO.
Pistache wird gegenwärtig aktiv gepflegt und lässt sich entweder aus den Quellen bauen oder im Falle von Ubuntu über ein vom Entwickler-Team gepflegtes Repository beziehen. Die Bibliothek setzt auf RapidJSON, welche derzeit eine der performantesten Implementierungen ist. Das ist entscheidend, da bei REST-Schnittstellen JSON das verbreitetste Austauschformat ist.
Pistache: Hello World!
Mit wenigen Zeilen Code ist die erste Applikation rasch geschrieben:
#include <pistache/endpoint .h> using namespace Pistache;class HelloHandler : public Http::Handler { public: HTTP_PROTOTYPE(HelloHandler) void onRequest(const Http::Request& request, Http::ResponseWriter response) override { response.send(Http::Code::Ok, "Hello from Pistache"); } };int main() { Http::listenAndServe<HelloHandler>(Address (Ipv4::any(), Port (8084)), Http::Endpoint::options ()); }
In der Subklasse der Http::Handler-Klasse muss die onRequest-Methode implementiert werden. Einfache Handler wie oben können das Makro HTTP_PROTOTYPE einsetzen, welches sich um die Definition der abstrakten (pure virtual) clone-Methode kümmert. In der main-Methode (dem Einstiegspunkt eines jeden C++-Programms) wird listenAndServe aufgerufen und Address – welche hier den Port beinhaltet – sowie Option – das beispielsweise die Anzahl Threads oder Größenbeschränkungen für Anfragen und Antworten beinhalten könnte – mitgegeben.
Es empfiehlt sich, das Projekt mit CMake aufzusetzen. Ein entsprechendes Beispiel dafür und sämtliche im Dokument erwähnte Codes können im Repository eingesehen werden. Der Test im Browser mit der URL localhost:8084 zeigt nach Kompilation und Ausführung das Ergebnis:
Stand: 08.12.2025
Es ist für uns eine Selbstverständlichkeit, dass wir verantwortungsvoll mit Ihren personenbezogenen Daten umgehen. Sofern wir personenbezogene Daten von Ihnen erheben, verarbeiten wir diese unter Beachtung der geltenden Datenschutzvorschriften. Detaillierte Informationen finden Sie in unserer Datenschutzerklärung.
Einwilligung in die Verwendung von Daten zu Werbezwecken
Ich bin damit einverstanden, dass die Vogel IT-Medien GmbH, Max-Josef-Metzger-Straße 21, 86157 Augsburg, einschließlich aller mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen (im weiteren: Vogel Communications Group) meine E-Mail-Adresse für die Zusendung von Newslettern und Werbung nutzt. Auflistungen der jeweils zugehörigen Unternehmen können hier abgerufen werden.
Der Newsletterinhalt erstreckt sich dabei auf Produkte und Dienstleistungen aller zuvor genannten Unternehmen, darunter beispielsweise Fachzeitschriften und Fachbücher, Veranstaltungen und Messen sowie veranstaltungsbezogene Produkte und Dienstleistungen, Print- und Digital-Mediaangebote und Services wie weitere (redaktionelle) Newsletter, Gewinnspiele, Lead-Kampagnen, Marktforschung im Online- und Offline-Bereich, fachspezifische Webportale und E-Learning-Angebote. Wenn auch meine persönliche Telefonnummer erhoben wurde, darf diese für die Unterbreitung von Angeboten der vorgenannten Produkte und Dienstleistungen der vorgenannten Unternehmen und Marktforschung genutzt werden.
Meine Einwilligung umfasst zudem die Verarbeitung meiner E-Mail-Adresse und Telefonnummer für den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern wie z.B. LinkedIN, Google und Meta. Hierfür darf die Vogel Communications Group die genannten Daten gehasht an Werbepartner übermitteln, die diese Daten dann nutzen, um feststellen zu können, ob ich ebenfalls Mitglied auf den besagten Werbepartnerportalen bin. Die Vogel Communications Group nutzt diese Funktion zu Zwecken des Retargeting (Upselling, Crossselling und Kundenbindung), der Generierung von sog. Lookalike Audiences zur Neukundengewinnung und als Ausschlussgrundlage für laufende Werbekampagnen. Weitere Informationen kann ich dem Abschnitt „Datenabgleich zu Marketingzwecken“ in der Datenschutzerklärung entnehmen.
Falls ich im Internet auf Portalen der Vogel Communications Group einschließlich deren mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen geschützte Inhalte abrufe, muss ich mich mit weiteren Daten für den Zugang zu diesen Inhalten registrieren. Im Gegenzug für diesen gebührenlosen Zugang zu redaktionellen Inhalten dürfen meine Daten im Sinne dieser Einwilligung für die hier genannten Zwecke verwendet werden. Dies gilt nicht für den Datenabgleich zu Marketingzwecken.
Recht auf Widerruf
Mir ist bewusst, dass ich diese Einwilligung jederzeit für die Zukunft widerrufen kann. Durch meinen Widerruf wird die Rechtmäßigkeit der aufgrund meiner Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt. Um meinen Widerruf zu erklären, kann ich als eine Möglichkeit das unter https://contact.vogel.de abrufbare Kontaktformular nutzen. Sofern ich einzelne von mir abonnierte Newsletter nicht mehr erhalten möchte, kann ich darüber hinaus auch den am Ende eines Newsletters eingebundenen Abmeldelink anklicken. Weitere Informationen zu meinem Widerrufsrecht und dessen Ausübung sowie zu den Folgen meines Widerrufs finde ich in der Datenschutzerklärung.
Hello from Pistache
Docker
Damit die Applikation in der Cloud orchestriert verwendet werden kann, ist ein Docker-Image die Grundlage. Basierend auf dem schlanken Alpine Linux kann das Image so aussehen:
FROM alpine:latest RUN apk -U upgrade && apk add --no-cache git clang g++ rapidjson-dev libstdc++ meson && \ git clone https://github.com/pistacheio/pistache.git && \ cd pistache && \ meson setup build && \ meson install -C build && \ cd - && rm -r pistache && \ apk del --no-cache --purge g++ clang mesonCOPY . pistache RUN apk add --no-cache --purge g++ clang cmake make && \ export CXX=clang++ && export CC=clang && \ cd pistache && mkdir build && cd build && \ cmake .. -DCMAKE_BUILD_TYPE=Release && \ make && \ mv pistachedemo /usr/bin/pistachedemo && \ cd ../.. && rm -r pistache && \ apk del --no-cache --purge g++ clang cmake make CMD ["pistachedemo"] EXPOSE 8084 8084
Pistache wird mit dem Build-System Meson gebaut und installiert. Es wurde sichergestellt, dass die beiden RUN-Abschnitte die nicht mehr verwendeten Abhängigkeiten wieder freigeben, um das resultierende Image so klein wie möglich zu gestalten.
Vergleich mit anderen Technologien
Gegenwärtig sind Spring Native und Quarkus in der GraalVM sowie die Implementierung in der Programmiersprache Go zusammen mit dem GIN-Framework plausible Alternativen zu der wohl verbreitetsten Implementierung von Microservices mit Spring Boot in der JVM. Um einen objektiven Vergleich der verschiedenen Varianten erstellen zu können, wurde jeweils ein Endpunkt erstellt, welcher Primzahlen bis zu einem bestimmten Limit berechnet und die Anzahl der Resultate als JSON zurückgibt. Dafür wurde das Sieb von Atkin als Berechnungsgrundlage verwendet. Sämtlicher Code ist im erwähnten Repository zu finden.
Build
Beim generierten Dockerfile führt Pistache mit rund 20 MB für das gesamte Image.
(Bild: adesso Schweiz AG)
Beim Vergleich der Build-Zeiten gewinnt ganz klar Go, der ungefähr in einer Sekunde baut. Gefolgt von der Umsetzung in C++. Das Erstellen der ausführbaren Dateien aus dem Java-Code ist sehr zeit- und arbeitsspeicherintensiv. Während des Build-Prozesses, der mehrere Minuten dauert, werden über 6 Gigabyte (GB) RAM vom Host in Anspruch genommen. Im CI/CD-Umfeld und einem bestimmten Volumen an Projekten, könnte dies ein ernstzunehmendes Problem darstellen.
Performance
Bei der Startzeit haben Pistache und Go die Nase vorn.
(Bild: adesso Schweiz AG)
Mit der Startzeit wird die Zeit vom Starten der Applikation bis zum Verarbeiten des ersten Requests gemessen. Sowohl Pistache als auch Go starten in weniger als einer Millisekunde. Quarkus und Spring Native benötigen etwas mehr Zeit, liefern jedoch deutlich bessere Ergebnisse als das Starten der SpringBoot-Applikation in der JVM.
Den zeitliche Verlauf der Response Time gemessen in einem JMeter-Test.
(Bild: adesso Schweiz AG)
Bei den Antwortzeiten bleibt Pistache ungeschlagen. Gegenüber Go antwortet Pistache im Test in der Hälfte der Zeit. Zur Laufzeit verhält sich die SpringBoot-Applikation, was die Performance angeht, nicht schlecht. In der vorngestellten Grafik zeigt sich, dass die Ergebnisse besser werden, je länger die Applikation betrieben wird. Leider sehen die Ergebnisse bei den GraalVM-basierten Microservices weniger gut aus.
Ressourcen
Maschinennähe zahlt sich bei der Ressourcennutzung aus.
(Bild: adesso Schweiz AG)
Die C++-Implementierung geht wie erwartet besonders schonend mit den Ressourcen um und hebt sich deutlich von den verglichenen Java-Umsetzungen und der Go-Applikation ab.
OpenAPI
Von offizieller Seite gibt es Unterstützung für Pistache im Swagger-Codegen-Tool, welches auch direkt in SwaggerHub ausgeführt werden kann. Dazu gibt es das Profil pistache-server, um den Server-Stub zu erzeugen. Der generierte Code enthält Templates, welche dann implementiert werden müssen, wie das bei allen anderen Sprachen und Frameworks der Fall ist.
Ebenso ist es möglich, die Definition aus bestehendem Code zu generieren. Dafür wird die Definition Klasse verwendet und damit ist die Request-Beschreibung sichergestellt. Die Beschreibung der DTOs, welche zurückgeliefert werden, gestaltet sich allerdings schwieriger. Wer aus dem Java-Umfeld gewohnt ist, dass die entsprechenden Klassen einfach mit Annotationen versehen werden können, wird hier eine große Enttäuschung erleben.
Mangels Reflection bei C++ ist dies leider nicht so einfach möglich. Es ist dennoch empfehlenswert, den Microservice auf der Definition aufzubauen. Es werden der Pfad, der MIME-Type und entsprechende Beschreibungen wie nachfolgend gezeigt definiert. Zudem wird über die bind-Methode die effektive Implementierung angebunden. Im nachfolgenden Beispiel ist die Initialisierung in eine Methode ausgelagert worden:
void createDescription() { description.route (description.get("/")) .bind(&Server::hello) .produces(MIME(Text, Plain)) .response(Http::Code::Ok, "HelloWorld!"); description.route(description.get("/primes"), "Calculate primes") .bind(&Server::primes) .produces(MIME(Application, Json)) .parameter<Rest::Type::Integer>("limit", "Limit of the calculation") .response(Http::Code::Ok, "Total and list of primes"); }
Im gezippten Beispiel sind es die Methoden hello und primes, in welchen die Request-Abwicklung stattfindet. Es wird darin schließlich die send-Methode vom ResponseWriter aufgerufen. Die gezeigte Definition wird einer Router-Instanz der initFromDescription übergeben. Daraus kann dann der Handler für den HttpEndpoint gesetzt, bevor dieser gestartet wird. Letzterer muss zuvor initialisiert worden sein.
Schonender Umgang mit Ressourcen und hohe Performance müssen keine Gegensätze sein, wie im Vergleich deutlich aufgezeigt werden konnte. Soll ein Microservice effizient und sparsam umgesetzt werden, ist Pistache ganz klar eine sehr interessante Option, mit der auch ein bescheidener Beitrag zum Schutz der Umwelt geleistet werden kann.
Gerade für kleine Anwendungen mit geringer Komplexität ist die Technologie besonders geeignet. Erwähnenswert ist zudem, dass Promises/A+ für die Verarbeitung für JavaScript Promises von Pistache unterstützt werden.
Nebst der erschwerten Generierung von umfangreicher API-Dokumentation, muss jedoch ein weiterer Schwachpunkt erwähnt werden: Es handelt sich dabei um die bestehende Dokumentation von Pistache, die stark zu wünschen übriglässt. Bis auf wenige Implementierungsbeispiele ist zum heutigen Zeitpunkt kaum etwas vorhanden.
Eduardo Hahn Paredes
(Bild: adesso Schweiz AG)
Fairerweise muss aber auch berücksichtigt werden, dass der 1.0-Meilenstein der Bibliothek noch nicht erreicht wurde. Gemäß README im Github-Repository von Pistache ist der produktive Einsatz als Server jedoch bereits möglich. Im Client-Teil, welcher dieser Artikel nicht behandelt, scheint es noch Probleme zu geben. Man darf also gespannt sein, was man in Zukunft noch von Pistache hören wird.
* Eduardo Hahn Paredes ist Team Leader und Backend-Entwickler bei der adesso Schweiz AG. Er hat 20 Jahre IT-Erfahrung in zahlreichen Technologien. In den vergangenen Jahren lag sein Fokus hauptsächlich auf Spring Boot und OpenShift.