Einführung in JBoss Undertow

Einführung in JBoss Undertow

1. Überblick

Undertow is an extremely lightweight and high-performance web server from JBoss. Unterstützt sowohl blockierende als auch nicht blockierende APIs mitNIO.

Da es sich um Java handelt, kann es in allen JVM-basierten Anwendungen im eingebetteten Modus verwendet werden. Selbst derWilfFly-Server von JBoss verwendet internUndertow, um die Leistung des Servers zu verbessern.

In diesem Tutorial zeigen wir Ihnen die Funktionen von Undertow und deren Verwendung.

2. Warum unterziehen?

  • Leichtgewicht:Undertow ist mit unter 1 MB extrem leicht. Im eingebetteten Modus werden zur Laufzeit nur 4 MB Heapspeicher verwendet

  • Servlet 3.1: Es unterstütztServlet 3.1 vollständig

  • Web Socket: Unterstützt die Web Socket-Funktionalität (einschließlichJSR-356).

  • Persistente Verbindung: Standardmäßig enthältUndertow persistente HTTP-Verbindungen, indem der Antwortheader vonkeep-alivehinzugefügt wird. Es hilft Clients, die dauerhafte Verbindungen unterstützen, die Leistung zu optimieren, indem Verbindungsdetails wiederverwendet werden

3. Undertow verwenden

Beginnen wir mit der Verwendung vonUndertow, indem wir einen einfachen Webserver erstellen.

3.1. Maven-Abhängigkeit

UmUndertow zu verwenden, müssen wir unserenpom.xml die folgende Abhängigkeit hinzufügen:


    io.undertow
    undertow-servlet
    1.4.18.Final

Um ein lauffähiges Glas zu erstellen, müssen wir auchmaven-shade-plugin hinzufügen. Aus diesem Grund müssen wir auch folgende Konfiguration hinzufügen:


    org.apache.maven.plugins
    maven-shade-plugin
    
        
            package
            
                shade
            
        
    

Die neueste Version vonUndertow ist inCentral Maven Repository verfügbar.

3.2. Einfacher Server

Mit dem folgenden Codefragment können wir einen einfachen Webserver mithilfe derBuilder-API von Undertow erstellen:

public class SimpleServer {
    public static void main(String[] args) {
        Undertow server = Undertow.builder().addHttpListener(8080,
          "localhost").setHandler(exchange -> {
            exchange.getResponseHeaders()
            .put(Headers.CONTENT_TYPE, "text/plain");
          exchange.getResponseSender().send("Hello example");
        }).build();
        server.start();
    }
}

Hier haben wir dieBuilder-API verwendet, um den8080-Port an diesen Server zu binden. Beachten Sie auch, dass wir einen Lambda-Ausdruck verwendet haben, um den Handler zu verwenden.

Wir können auch das folgende Code-Snippet verwenden, um dasselbe zu tun, ohne Lambda-Ausdrücke zu verwenden:

Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
  .setHandler(new HttpHandler() {
      @Override
      public void handleRequest(HttpServerExchange exchange)
        throws Exception {
          exchange.getResponseHeaders().put(
            Headers.CONTENT_TYPE, "text/plain");
          exchange.getResponseSender().send("Hello example");
      }
  }).build();

Wichtig hierbei ist die Verwendung derHttpHandler API. It’s the most important plugin to customize an Undertow application based on our needs.

In diesem Fall haben wir einen benutzerdefinierten Handler hinzugefügt, der bei jeder Anforderung den AntwortheaderContent-Type: text/plainhinzufügt.

Auf ähnliche Weise können wir, wenn wir bei jeder Antwort einen Standardtext zurückgeben möchten, den folgenden Codeausschnitt verwenden:

exchange.getResponseSender()
  .send("Hello example");

3.3. Sicherer Zugang

In den meisten Fällen erlauben wir nicht allen Benutzern den Zugriff auf unseren Server. Normalerweise können Benutzer mit gültigen Anmeldeinformationen Zugriff erhalten. Wir können denselben Mechanismus mitUndertow implementieren.

Um dies zu implementieren, müssen wir einen Identitätsmanager erstellen, der die Authentizität des Benutzers für jede Anforderung überprüft.

Wir können UndertowsIdentityManagerdafür verwenden:

public class CustomIdentityManager implements IdentityManager {
    private Map users;

    // standard constructors

    @Override
    public Account verify(Account account) {
        return account;
    }

    @Override
    public Account verify(Credential credential) {
        return null;
    }

    @Override
    public Account verify(String id, Credential credential) {
        Account account = getAccount(id);
        if (account != null && verifyCredential(account, credential)) {
            return account;
        }
        return null;
    }
}

Sobald der Identitätsmanager erstellt ist, müssen wir einen Bereich erstellen, der die Benutzeranmeldeinformationen enthält:

private static HttpHandler addSecurity(
  HttpHandler toWrap,
  IdentityManager identityManager) {

    HttpHandler handler = toWrap;
    handler = new AuthenticationCallHandler(handler);
    handler = new AuthenticationConstraintHandler(handler);
    List mechanisms = Collections.singletonList(
      new BasicAuthenticationMechanism("example_Realm"));
    handler = new AuthenticationMechanismsHandler(handler, mechanisms);
    handler = new SecurityInitialHandler(
      AuthenticationMode.PRO_ACTIVE, identityManager, handler);
    return handler;
}

Hier haben wirAuthenticationMode alsPRO_ACTIVE verwendet, was bedeutet, dass jede an diesen Server eingehende Anforderung an die definierten Authentifizierungsmechanismen weitergeleitet wird, um die Authentifizierung eifrig durchzuführen.

Wenn wirAuthenticationMode alsCONSTRAINT_DRIVEN definieren, durchlaufen nur diese Anforderungen die definierten Authentifizierungsmechanismen, bei denen die Einschränkung (en) ausgelöst werden, die die Authentifizierung vorschreiben.

Jetzt müssen wir diesen Bereich und den Identitätsmanager nur noch dem Server zuordnen, bevor er gestartet wird:

public static void main(String[] args) {
    Map users = new HashMap<>(2);
    users.put("root", "password".toCharArray());
    users.put("admin", "password".toCharArray());

    IdentityManager idm = new CustomIdentityManager(users);

    Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
      .setHandler(addSecurity(e -> setExchange(e), idm)).build();

    server.start();
}

private static void setExchange(HttpServerExchange exchange) {
    SecurityContext context = exchange.getSecurityContext();
    exchange.getResponseSender().send("Hello " +
      context.getAuthenticatedAccount().getPrincipal().getName(),
      IoCallback.END_EXCHANGE);
}

Hier haben wir zwei Benutzerinstanzen mit Anmeldeinformationen erstellt. Sobald der Server hochgefahren ist, müssen Sie einen dieser beiden Anmeldeinformationen verwenden, um darauf zugreifen zu können.

3.4. Web Socket

Es ist einfach, einen Web-Socket-Austauschkanal mit der API vonUnderTow’s WebSocketHttpExchangezu erstellen.

Zum Beispiel können wir einen Socket-Kommunikationskanal auf PfadexampleApp mit dem folgenden Code-Snippet öffnen:

public static void main(String[] args) {
    Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
      .setHandler(path().addPrefixPath("/exampleApp", websocket(
        (exchange, channel) -> {
          channel.getReceiveSetter().set(getListener());
          channel.resumeReceives();
      })).addPrefixPath("/", resource(new ClassPathResourceManager(
        SocketServer.class.getClassLoader(),
        SocketServer.class.getPackage())).addWelcomeFiles("index.html")))
        .build();

    server.start();
}

private static AbstractReceiveListener getListener() {
    return new AbstractReceiveListener() {
        @Override
        protected void onFullTextMessage(WebSocketChannel channel,
          BufferedTextMessage message) {
            String messageData = message.getData();
            for (WebSocketChannel session : channel.getPeerConnections()) {
                WebSockets.sendText(messageData, session, null);
            }
        }
    };
}

Wir können eine HTML-Seite mit dem Namenindex.html erstellen und dieWebSocket-API von JavaScript verwenden, um eine Verbindung zu diesem Kanal herzustellen.

3.5. Dateiserver

MitUndertow können wir auch einen Dateiserver erstellen, der Verzeichnisinhalte anzeigen und Dateien direkt aus dem Verzeichnis bereitstellen kann:

public static void main( String[] args ) {
    Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
        .setHandler(resource(new PathResourceManager(
          Paths.get(System.getProperty("user.home")), 100 ))
        .setDirectoryListingEnabled( true ))
        .build();
    server.start();
}

Wir müssen keinen UI-Inhalt erstellen, um den Verzeichnisinhalt anzuzeigen. Out-of-the-BoxUndertow bietet eine Seite für diese Anzeigefunktion.

4. Spring Boot Plugin

Abgesehen vonTomcat undJetty, unterstütztSpring BootUnderTow als eingebetteten Servlet-Container. UmUndertow zu verwenden, müssen wir die folgenden Abhängigkeiten inpom.xml: einfügen


    org.springframework.boot
    spring-boot-starter-undertow
    1.5.6.RELEASE

Die neueste Version vonSpring Boot Undertow plugin ist inCentral Maven Repository verfügbar.

5. Fazit

In diesem Artikel haben wir etwas überUndertow gelernt und wie wir damit verschiedene Servertypen erstellen können.

Wie immer ist der vollständige Quellcodeover on GitHub verfügbar.