Перейти к основному содержимому

Введение в JBoss Undertow

· 5 мин. чтения

1. Обзор

Undertow — чрезвычайно легкий и высокопроизводительный веб-сервер от JBoss . Он поддерживает как блокирующие, так и неблокирующие API с NIO .

Поскольку он написан на Java, его можно использовать в любых приложениях на основе JVM во встроенном режиме, даже сервер JBoss WilfFly внутри использует Undertow для повышения производительности сервера.

В этом уроке мы покажем особенности Undertow и способы его использования.

2. Почему подводное течение?

  • Легкий: Undertow чрезвычайно легкий - менее 1 МБ. Во встроенном режиме он использует только 4 МБ кучи во время выполнения.
  • Servlet 3.1: полностью поддерживает Servlet 3.1 .
  • Web Socket: поддерживает функциональность Web Socket (включая JSR-356 ) .
  • Постоянное соединение: по умолчанию Undertow включает постоянные соединения HTTP, добавляя заголовок ответа проверки активности. Это помогает клиентам, поддерживающим постоянные соединения, оптимизировать производительность за счет повторного использования сведений о соединении.

3. Использование отлива

Давайте начнем использовать Undertow , создав простой веб-сервер.

3.1. Зависимость от Maven

Чтобы использовать Undertow , нам нужно добавить следующую зависимость в наш pom.xml :

<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<version>1.4.18.Final</version>
</dependency>

Чтобы создать работающий jar, нам также нужно добавить maven-shade-plugin . Вот почему нам также необходимо добавить следующую конфигурацию:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>

Последняя версия Undertow доступна в Central Maven Repository .

3.2. Простой сервер

С помощью приведенного ниже фрагмента кода мы можем создать простой веб-сервер, используя API Undertow Builder :

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 ForEach");
}).build();
server.start();
}
}

Здесь мы использовали API Builder для привязки порта 8080 к этому серверу. Также обратите внимание, что мы использовали лямбда-выражение для использования обработчика.

Мы также можем использовать приведенный ниже фрагмент кода, чтобы сделать то же самое без использования лямбда-выражений:

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 ForEach");
}
}).build();

Здесь важно отметить использование HttpHandler API. Это самый важный плагин для настройки приложения Undertow в соответствии с нашими потребностями.

В этом случае мы добавили настраиваемый обработчик, который будет добавлять заголовок ответа Content-Type: text/plain к каждому запросу.

Аналогичным образом, если мы хотим вернуть некоторый текст по умолчанию с каждым ответом, мы можем использовать приведенный ниже фрагмент кода:

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

3.3. Безопасный доступ

В большинстве случаев мы не разрешаем всем пользователям доступ к нашему серверу. Обычно доступ могут получить пользователи с действительными учетными данными. Мы можем реализовать тот же механизм с Undertow .

Чтобы реализовать это, нам нужно создать менеджер удостоверений, который будет проверять подлинность пользователя для каждого запроса.

Для этого мы можем использовать IdentityManager Undertow :

public class CustomIdentityManager implements IdentityManager {
private Map<String, char[]> 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;
}
}

После создания диспетчера удостоверений нам нужно создать область, в которой будут храниться учетные данные пользователя:

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

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

Здесь мы использовали AuthenticationMode как PRO_ACTIVE , что означает, что каждый запрос, поступающий на этот сервер, будет передан определенным механизмам аутентификации для выполнения аутентификации с готовностью.

Если мы определим AuthenticationMode как CONSTRAINT_DRIVEN , то только те запросы будут проходить через определенные механизмы аутентификации, где срабатывает ограничение(я), которое предписывает аутентификацию.

Теперь нам просто нужно сопоставить эту область и диспетчер удостоверений с сервером перед его запуском:

public static void main(String[] args) {
Map<String, char[]> 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);
}

Здесь мы создали два пользовательских экземпляра с учетными данными. Когда сервер запущен, для доступа к нему нам нужно использовать любой из этих двух учетных данных.

3.4. Веб-сокет

Создать канал обмена веб-сокетами с помощью API UnderTow WebSocketHttpExchange несложно.

Например, мы можем открыть канал связи сокета по пути foreachApp с помощью приведенного ниже фрагмента кода:

public static void main(String[] args) {
Undertow server = Undertow.builder().addHttpListener(8080, "localhost")
.setHandler(path().addPrefixPath("/foreachApp", 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);
}
}
};
}

Мы можем создать HTML-страницу с именем index.html и использовать JavaScript WebSocket API для подключения к этому каналу.

3.5. Файловый сервер

С помощью Undertow мы также можем создать файловый сервер, который может отображать содержимое каталога и напрямую обслуживать файлы из каталога:

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();
}

Нам не нужно создавать какой-либо контент пользовательского интерфейса для отображения содержимого каталога. Стандартный Undertow предоставляет страницу для этой функции отображения.

4. Плагин загрузки Spring

Помимо Tomcat и Jetty , Spring Boot поддерживает UnderTow в качестве встроенного контейнера сервлетов. Чтобы использовать Undertow , нам нужно добавить следующую зависимость в pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>

Последняя версия плагина Spring Boot Undertow доступна в [Центральном репозитории Maven](https://search.maven.org/classic/#search|gav|1|g%3A"org.springframework.boot" AND a%3A"spring-boot-starter-undertow") .

5. Вывод

В этой статье мы узнали о Undertow и о том, как мы можем создавать с его помощью различные типы серверов.

Как всегда, полный исходный код доступен на GitHub .