WebSocket reattivi con molla 5

1. Panoramica

In questo articolo, creeremo un rapido esempio utilizzando la nuova API WebSocket Spring 5 insieme alle funzionalità reattive fornite da Spring WebFlux.

WebSocket è un noto protocollo che consente la comunicazione full-duplex tra client e server, generalmente utilizzato nelle applicazioni web in cui il client e il server devono scambiare eventi ad alta frequenza e con bassa latenza.

Spring Framework 5 ha modernizzato il supporto WebSocket nel framework, aggiungendo funzionalità reattive a questo canale di comunicazione.

Possiamo trovare di più su Spring WebFlux qui.

2. Dipendenze di Maven

Useremo le dipendenze spring-boot-starters per spring-boot-integration e spring-boot-starter-webflux, attualmente disponibili su Spring Milestone Repository.

In questo esempio, stiamo usando l'ultima versione disponibile, 2.0.0.M7, ma si dovrebbe sempre ottenere l'ultima versione disponibile nel repository Maven:

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

3. Configurazione WebSocket in primavera

La nostra configurazione è piuttosto semplice: inietteremo WebSocketHandler per gestire la sessione socket nella nostra applicazione Spring WebSocket.

@Autowired private WebSocketHandler webSocketHandler; 

Inoltre, creiamo un metodo HandlerMapping con annotazioni sui bean che sarà responsabile della mappatura tra richieste e oggetti gestore:

@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; }

L'URL a cui possiamo connetterci sarà: ws: // localhost: / event-emitter.

4. Gestione dei messaggi WebSocket in primavera

La nostra classe ReactiveWebSocketHandler sarà responsabile della gestione della sessione WebSocket sul lato server.

Implementa l' interfaccia WebSocketHandler in modo da poter sovrascrivere il metodo handle , che verrà utilizzato per inviare il messaggio al client 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. Creazione di un semplice client WebSocket reattivo

Creiamo ora un client Spring Reactive WebSocket che sarà in grado di connettersi e scambiare informazioni con il nostro server WebSocket.

5.1. Dipendenza da Maven

Innanzitutto, le dipendenze di Maven.

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

Qui stiamo usando lo stesso flusso di avvio primaverile usato in precedenza per configurare la nostra applicazione server WebSocket reattiva.

5.2. WebSocket Client

Ora, creiamo la classe ReactiveClientWebSocket , responsabile dell'avvio della comunicazione con il server:

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

Nel codice sopra possiamo vedere che stiamo usando ReactorNettyWebSocketClient , che è l' implementazione di WebSocketClient da usare con Reactor Netty.

Inoltre, il client si connette al server WebSocket tramite l'URL ws: // localhost: 8080 / event-emitter, stabilendo una sessione non appena è connesso al server.

Possiamo anche vedere che stiamo inviando un messaggio al server (" event-spring-reattivo-client-websocket ") insieme alla richiesta di connessione.

Inoltre viene richiamato il metodo send , aspettandosi come parametro una variabile di tipo Publisher, che nel nostro caso il nostro Publisher è Mono e T è una semplice Stringa “ event-me-from-reattivo-java-client-websocket “.

Inoltre, viene invocato il metodo thenMany (…) che prevede un Flux di tipo String . Il metodo receive () ottiene il flusso dei messaggi in arrivo, che successivamente vengono convertiti in stringhe.

Infine, il metodo block () forza il client a disconnettersi dal server dopo il tempo specificato (10 secondi nel nostro esempio).

5.3. Avvio del client

Per eseguirlo, assicurati che Reactive WebSocket Server sia attivo e in esecuzione. Quindi, avvia la classe ReactiveJavaClientWebSocket e possiamo vedere nel registro di sysout gli eventi emessi:

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

Possiamo anche vedere nel log dal nostro server Reactive WebSocket il messaggio inviato dal client durante il tentativo di connessione:

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

Inoltre, possiamo vedere il messaggio di connessione terminata dopo che il client ha terminato le sue richieste (nel nostro caso, dopo i 10 secondi):

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

6. Creazione di un client WebSocket del browser

Creiamo un semplice client HTML / Javascript WebSocket per utilizzare la nostra applicazione server WebSocket reattiva.

 var clientWebSocket = new WebSocket("ws://localhost:8080/event-emitter"); clientWebSocket.onopen = function() { console.log("clientWebSocket.onopen", clientWebSocket); console.log("clientWebSocket.readyState", "websocketstatus"); clientWebSocket.send("event-me-from-browser"); } clientWebSocket.onclose = function(error) { console.log("clientWebSocket.onclose", clientWebSocket, error); events("Closing connection"); } clientWebSocket.onerror = function(error) { console.log("clientWebSocket.onerror", clientWebSocket, error); events("An error occured"); } clientWebSocket.onmessage = function(error) { console.log("clientWebSocket.onmessage", clientWebSocket, error); events(error.data); } function events(responseEvent) { document.querySelector(".events").innerHTML += responseEvent + "

"; }

Con il server WebSocket in esecuzione, aprendo questo file HTML in un browser (es: Chrome, Internet Explorer, Mozilla Firefox ecc.), Dovremmo vedere gli eventi stampati sullo schermo, con un ritardo di 1 secondo per evento, come definito in il nostro server 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. Conclusione

Qui abbiamo presentato un esempio di come creare una comunicazione WebSocket tra server e client utilizzando Spring 5 Framework, implementando le nuove funzionalità reattive fornite da Spring Webflux.

Come sempre, l'esempio completo può essere trovato nel nostro repository GitHub.