Реактивные WebSockets с Spring 5

Реактивные WebSockets с Spring 5

1. обзор

В этой статье мы собираемся создать быстрый пример использования нового Spring Framework 5 WebSockets API вместе с реактивными функциями, предоставляемыми Spring WebFlux Framework.

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

Spring Framework 5 модернизировал поддержку WebSockets в платформе, добавив реактивные возможности в этот канал связи.

Мы можем найти больше о Spring WebFluxhere.

2. Maven Зависимости

Мы собираемся использовать готовые зависимости от Spring-Boot-Starters дляspring-boot-integration иspring-boot-starter-webflux, которые в настоящее время доступны наSpring Milestone Repository.

В этом примере мы используем последнюю доступную версию 2.0.0.M7:


    org.springframework.boot
    spring-boot-starter-integration


    org.springframework.boot
    spring-boot-starter-webflux

3. Конфигурация WebSocket весной

Наша конфигурация довольно проста: мы введемWebSocketHandler для обработки сеанса сокета в нашем приложении Spring WebSocket.

@Autowired
private WebSocketHandler webSocketHandler;

Кроме того, давайте создадим метод, аннотированный bean-компонентомHandlerMapping, который будет отвечать за сопоставление запросов и объектов-обработчиков:

@Bean
public HandlerMapping webSocketHandlerMapping() {
    Map map = new HashMap<>();
    map.put("/event-emitter", webSocketHandler);

    SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
    handlerMapping.setOrder(1);
    handlerMapping.setUrlMap(map);
    return handlerMapping;
}

Здесь мы видим URL, который будет доступен для использования:ws://localhost:<port>/event-emitter.

4. Обработка сообщений WebSocket весной

Наш классReactiveWebSocketHandler будет отвечать за управление сеансом WebSocket на стороне сервера.

Он реализует интерфейсWebSocketHandler, поэтому мы можем переопределить методhandle, который будет использоваться для отправки сообщения клиенту WebSocket:

@Component
public class ReactiveWebSocketHandler implements WebSocketHandler {

    // private fields ...

    @Override
    public Mono handle(WebSocketSession webSocketSession) {
        return webSocketSession.send(intervalFlux
          .map(webSocketSession::textMessage))
          .and(webSocketSession.receive()
            .map(WebSocketMessage::getPayloadAsText)
            .log());
    }
}

5. Создание простого реактивного клиента WebSocket

Теперь давайте создадим клиент Spring Reactive WebSocket, который сможет подключаться и обмениваться информацией с нашим сервером WebSocket.

Во-первых, зависимости Maven.


    org.springframework.boot
    spring-boot-starter-webflux

Здесь мы используем тот же spring-boot-starter-webflux, который использовался ранее для настройки нашего реактивного серверного приложения WebSocket.

Теперь давайте создадим классReactiveClientWebSocket, отвечающий за запуск связи с сервером:

public class ReactiveJavaClientWebSocket {

    public static void main(String[] args) throws InterruptedException {

        WebSocketClient client = new ReactorNettyWebSocketClient();
        client.execute(
          URI.create("ws://localhost:8080/event-emitter"),
          session -> session.send(
            Mono.just(session.textMessage("event-spring-reactive-client-websocket")))
            .thenMany(session.receive()
              .map(WebSocketMessage::getPayloadAsText)
              .log())
            .then())
            .block(Duration.ofSeconds(10L));
    }
}

В приведенном выше коде мы видим, что мы используемReactorNettyWebSocketClient, который является реализациейWebSocketClient для использования с Reactor Netty.

Кроме того, клиентское соединение с сервером WebSocket через URLws://localhost:8080/event-emitter, устанавливает сеанс, как только он подключается к серверу.

Мы также можем видеть, что мы отправляем сообщение на сервер («event-spring-reactive-client-websocket») вместе с запросом на соединение.

Кроме того, вызывается методsend, ожидающий в качестве параметра переменной типаPublisher<T>,, который в нашем случаеPublisher<T> равенMono<T>, аT - простой Строка «event-me-from-reactive-java-client-websocket».

Более того, вызывается методthenMany(…), ожидающийFlux типаString. Методreceive() получает поток входящих сообщений, которые позже преобразуются в строки.

Наконец, методblock() заставляет клиента отключиться от сервера по истечении заданного времени (10 секунд в нашем примере).

Чтобы запустить его, убедитесь, что Reactive WebSocket Server запущен и работает. Затем запустите классReactiveJavaClientWebSocket, и мы сможем увидеть в журнале sysout генерируемые события:

[reactor-http-nio-4] INFO reactor.Flux.Map.1 -
onNext({"eventId":"6042b94f-fd02-47a1-911d-dacf97f12ba6",
"eventDt":"2018-01-11T23:29:26.900"})

Мы также можем увидеть в журнале с нашего сервера Reactive WebSocket сообщение, отправленное клиентом во время попытки подключения:

[reactor-http-nio-2] reactor.Flux.Map.1:
onNext(event-me-from-reactive-java-client)

Также мы можем увидеть сообщение о прекращении соединения после того, как клиент завершил свои запросы (в нашем случае, через 10 секунд):

[reactor-http-nio-2] reactor.Flux.Map.1: onComplete()

6. Создание клиента WebSocket в браузере

Давайте создадим простой клиент WebSocket HTML / Javascript для использования нашего реактивного серверного приложения WebSocket.

Когда сервер WebSocket запущен, открыв этот HTML-файл в браузере (например, Chrome, Internet Explorer, Mozilla Firefox и т. Д.), Мы должны увидеть события, выводимые на экран, с задержкой в ​​1 секунду для каждого события, как определено в наш сервер WebSocket.

{"eventId":"c25975de-6775-4b0b-b974-b396847878e6","eventDt":"2018-01-11T23:56:09.780"}
{"eventId":"ac74170b-1f71-49d3-8737-b3f9a8a352f9","eventDt":"2018-01-11T23:56:09.781"}
{"eventId":"40d8f305-f252-4c14-86d7-ed134d3e10c6","eventDt":"2018-01-11T23:56:09.782"}

7. Заключение

Здесь мы представили пример того, как создать соединение WebSocket между сервером и клиентом с помощью Spring 5 Framework, реализовав новые реактивные функции, предоставляемые Spring Webflux.

Как всегда, полный пример можно найти в нашемGitHub repository.