Während Frameworks wie Spring Boot auf einem Thread-pro-Request-Modell basieren — jeder HTTP-Request blockiert einen Thread — setzt Eclipse Vert.x von Grund auf auf asynchrone, nicht-blockierende Verarbeitung. Das Ergebnis: eine einzige JVM-Instanz kann zehntausende parallele Verbindungen handhaben, ohne dass der Thread-Pool kollabiert.

Das Event-Loop-Modell

Vert.x basiert auf dem Reactor-Pattern: Ein oder mehrere Event-Loop-Threads verarbeiten eingehende Ereignisse (HTTP-Requests, Timer, Nachrichten) in einer Endlosschleife. Blockierende Operationen sind tabu — sie würden den gesamten Event-Loop lahmlegen.

Event Loop (Thread 1) --> Handler 1 --> Handler 2 --> DB (non-blocking) --> Resume
Event Loop (Thread 2) --> Handler 3 --> HTTP-Call (non-blocking) --> Resume
Event Loop (Thread N) --> Handler 4 --> Handler 5 --> Handler 6 --> Done

Grundbaustein: Das Verticle

Die Grundeinheit in Vert.x ist das Verticle — eine leichtgewichtige Komponente, die vom Event-Loop ausgeführt wird:

import io.vertx.core.VerticleBase;
import io.vertx.core.Future;
import io.vertx.ext.web.Router;

public class HttpServerVerticle extends VerticleBase {

    @Override
    public Future<?> start() {
        Router router = Router.router(vertx);

        router.get("/hello").handler(ctx -> {
            ctx.response()
                .putHeader("content-type", "text/plain")
                .end("Hallo von Vert.x 5!");
        });

        return vertx.createHttpServer()
            .requestHandler(router)
            .listen(8080)
            .onSuccess(server ->
                System.out.println("Server läuft auf Port " + server.actualPort())
            );
    }
}
Code-Sprache: JavaScript (javascript)

Alle Handler-Callbacks werden vom Event-Loop ausgeführt — niemals blockieren!

Der Event Bus

Kommunikation zwischen Verticles erfolgt über den Event Bus, eine verteilte, lose gekoppelte Nachrichten-Infrastruktur:

<em>// Consumer-Verticle</em>
public class OrderVerticle extends VerticleBase {
    @Override
    public Future<?> start() {
        vertx.eventBus().consumer("bestellung.neu", message -> {
            String payload = (String) message.body();
            System.out.println("Neue Bestellung: " + payload);
            message.reply("Bestellung verarbeitet");
        });
        return Future.succeededFuture();
    }
}

<em>// Producer-Verticle</em>
public class ProducerVerticle extends VerticleBase {
    @Override
    public Future<?> start() {
        return vertx.eventBus().request("bestellung.neu", "B-1234")
            .onSuccess(reply -> System.out.println("Antwort: " + reply.body()))
            .onFailure(err -> System.err.println("Fehler: " + err.getMessage()));
    }
}
Code-Sprache: PHP (php)

Der Event Bus unterstützt Publish/Subscribe, Request/Response und kann — mit Clustering — sogar JVM-übergreifend kommunizieren.

Nicht-blockierender Datenbankzugriff

Blockierende JDBC-Aufrufe sind im Event-Loop tödlich. Vert.x bietet mit dem SQL Client eine vollständig asynchrone Alternative:

import io.vertx.jdbcclient.JDBCPool;
import io.vertx.jdbcclient.JDBCConnectOptions;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions;

<em>// Pool anlegen</em>
JDBCConnectOptions connectOptions = new JDBCConnectOptions()
    .setJdbcUrl("jdbc:postgresql://localhost:5432/shop");
PoolOptions poolOptions = new PoolOptions()
    .setMaxSize(16);
Pool pool = JDBCPool.pool(vertx, connectOptions, poolOptions);

<em>// Asynchrone Query</em>
pool.query("SELECT * FROM produkte WHERE preis > 19.99")
    .execute()
    .onSuccess(rows -> {
        for (var row : rows) {
            System.out.println(row.getString("name"));
        }
    })
    .onFailure(err -> {
        System.err.println("Fehler: " + err.getMessage());
    });
Code-Sprache: JavaScript (javascript)

Für PostgreSQL steht zudem der PgClient bereit — ein nativer, reaktiver Client, der ohne JDBC-Layer direkt über das PostgreSQL-Wire-Protokoll kommuniziert und seit Vert.x 5 über den PgBuilder konfiguriert wird.

Reaktive Alternativen: Mutiny und Virtual Threads

Neben RxJava unterstützt Vert.x 5 auch SmallRye Mutiny — eine eigenständige reaktive Programmierbibliothek, die als moderne Alternative zu RxJava positioniert ist. Mutiny bietet zwei Programmiermodelle: eine ereignisgesteuerte API (Multi/Uni) und eine imperative, Coroutine-ähnliche Variante.

Für Entwickler, die blockierende APIs anbinden müssen, bietet Vert.x seit dem Virtual-Threads-Incubator eine elegante Lösung: Blockierende Aufrufe können in virtuellen Threads ausgeführt werden, ohne den Event-Loop zu blockieren. Seit Java 21 sind Virtual Threads fester Bestandteil der Plattform:

vertx.executeBlocking(() -> {
    <em>// Läuft in einem virtuellen Thread — blockierende Aufrufe sind hier sicher</em>
    return legacyBlockingService.fetchData();
}).onSuccess(result -> {
    <em>// Zurück im Event-Loop</em>
    System.out.println("Ergebnis: " + result);
});
Code-Sprache: HTML, XML (xml)

Reaktive Streams mit RxJava

Vert.x lässt sich direkt mit RxJava kombinieren (Pull-Abhängigkeit vertx-rx-java3):

import io.vertx.rxjava3.core.AbstractVerticle;
import io.vertx.rxjava3.ext.web.Router;

public class RxVerticle extends AbstractVerticle {
    @Override
    public void start() {
        Router router = Router.router(vertx);

        router.get("/users/:id").handler(ctx -> {
            String id = ctx.pathParam("id");

            dbClient.preparedQuery("SELECT * FROM users WHERE id = $1")
                .rxExecute(Tuple.of(Integer.parseInt(id)))
                .map(rows -> rows.iterator().next())
                .subscribe(
                    row -> ctx.response().end(row.toJson().encode()),
                    err -> ctx.fail(500, err)
                );
        });
    }
}
Code-Sprache: JavaScript (javascript)

.rxExecute() wandelt das Future-basierte Vert.x-API in eine RxJava-Single um — für fließende reaktive Pipelines.

Wann Vert.x?

Vert.x ist besonders interessant für:

  • Hochskalierende Echtzeit-Anwendungen (Chat, Gaming, Börsenticker)
  • API-Gateways und Proxies mit minimalem Overhead
  • Teams, die ein reaktives Modell mit leichtgewichtigem Toolkit einem Full-Stack-Framework vorziehen
  • Polyglotte Projekte (Vert.x unterstützt Java, Kotlin, Groovy und JavaScript)
  • Microservices mit nativer Unterstützung für OpenTelemetry, Health Checks und Circuit Breaker

Fazit

Vert.x ist kein Framework, sondern ein Toolkit: leichtgewichtig, unaufdringlich und extrem performant. Es zwingt Entwickler, in Ereignissen zu denken — blockierende Aufrufe sind strukturell unmöglich. Wer bereit ist, das Future-basierte Programmiermodell (oder RxJava) zu akzeptieren, wird mit einer Skalierbarkeit belohnt, die traditionelle Servlet-basierte Frameworks nicht erreichen. Mit der aktuellen Version 5.1.2 ist Vert.x erwachsen geworden — stabil, gut dokumentiert und produktionserprobt.