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 .