REST vs WebSocket

1. Panoramica

In questo tutorial, esamineremo le basi della comunicazione client-server e lo esploreremo attraverso due opzioni popolari disponibili oggi. Vedremo come WebSocket, che è un nuovo concorrente, se la cava con la scelta più popolare di RESTful HTTP.

2. Nozioni di base sulla comunicazione di rete

Prima di approfondire i dettagli delle diverse opzioni e dei loro pregi e demeriti, aggiorniamo rapidamente il panorama della comunicazione di rete. Ciò aiuterà a mettere le cose in prospettiva e capirlo meglio.

Le comunicazioni di rete possono essere meglio comprese in termini di modello OSI (Open Systems Interconnection).

Il modello OSI divide il sistema di comunicazione in sette livelli di astrazione:

Nella parte superiore di questo modello c'è il livello Applicazione che è di nostro interesse in questo tutorial. Tuttavia, discuteremo alcuni aspetti nei primi quattro livelli mentre procediamo confrontando WebSocket e RESTful HTTP.

Il livello dell'applicazione è il più vicino all'utente finale ed è responsabile dell'interfacciamento con le applicazioni che partecipano alla comunicazione. Esistono diversi protocolli popolari utilizzati in questo livello come FTP, SMTP, SNMP, HTTP e WebSocket.

3. Descrivere WebSocket e HTTP RESTful

Sebbene la comunicazione possa avvenire tra un numero qualsiasi di sistemi, siamo particolarmente interessati alla comunicazione client-server. Più specificamente, ci concentreremo sulla comunicazione tra un browser web e un server web. Questo è il frame che useremo per confrontare WebSocket con RESTful HTTP.

Ma prima di procedere oltre, perché non capire subito cosa sono!

3.1. WebSocket

Secondo la definizione formale, WebSocket è un protocollo di comunicazione che offre una comunicazione bidirezionale full-duplex su una connessione TCP persistente. Ora, comprenderemo ogni parte di questa affermazione in dettaglio mentre procediamo.

WebSocket è stato standardizzato come protocollo di comunicazione da IETF come RFC 6455 nel 2011. La maggior parte dei browser Web moderni oggi supporta il protocollo WebSocket.

3.2. HTTP RESTful

Sebbene tutti conosciamo HTTP a causa della sua presenza onnipresente su Internet, è anche un protocollo di comunicazione a livello di applicazione. HTTP è un protocollo basato su richiesta-risposta , di nuovo lo capiremo meglio più avanti nel tutorial.

REST (Representational State Transfer) è uno stile architettonico che pone una serie di vincoli su HTTP per creare servizi web.

4. Sottoprotocollo WebSocket

Sebbene WebSocket definisca un protocollo per la comunicazione bidirezionale tra client e server, non pone alcuna condizione sul messaggio da scambiare . Questo è lasciato aperto alle parti nella comunicazione per concordare come parte della negoziazione del sottoprotocollo.

Non è conveniente sviluppare un sottoprotocollo per applicazioni non banali. Fortunatamente, ci sono molti sottoprotocolli popolari come STOMP disponibili per l'uso . STOMP è l'acronimo di Simple Text Oriented Messaging Protocol e funziona su WebSocket. Spring Boot ha il supporto di prima classe per STOMP, che utilizzeremo nel nostro tutorial.

5. Installazione rapida in Spring Boot

Non c'è niente di meglio che vedere un esempio funzionante. Quindi, creeremo semplici casi d'uso sia in WebSocket che in HTTP RESTful per esplorarli ulteriormente e quindi confrontarli. Creiamo un semplice componente server e client per entrambi.

Creeremo un semplice client utilizzando JavaScript che invierà un nome. E creeremo un server utilizzando Java che risponderà con un saluto.

5.1. WebSocket

Per utilizzare WebSocket in Spring Boot, avremo bisogno dello starter appropriato:

 org.springframework.boot spring-boot-starter-websocket 

Ora configureremo gli endpoint STOMP:

@Configuration @EnableWebSocketMessageBroker public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws"); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.setApplicationDestinationPrefixes("/app"); config.enableSimpleBroker("/topic"); } }

Definiamo rapidamente un semplice server WebSocket che accetta un nome e risponde con un saluto:

@Controller public class WebSocketController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(Message message) throws Exception { return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); } }

Infine, costruiamo il client per comunicare con questo server WebSocket. Dato che stiamo enfatizzando la comunicazione da browser a server, creiamo un client in JavaScript:

var stompClient = null; function connect() { stompClient = Stomp.client('ws://localhost:8080/ws'); stompClient.connect({}, function (frame) { stompClient.subscribe('/topic/greetings', function (response) { showGreeting(JSON.parse(response.body).content); }); }); } function sendName() { stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()})); } function showGreeting(message) { $("#greetings").append("" + message + ""); }

Questo completa il nostro esempio funzionante di un server e client WebSocket. C'è una pagina HTML nel repository di codice che fornisce una semplice interfaccia utente con cui interagire.

Anche se questo graffia solo la superficie, WebSocket con Spring può essere utilizzato per creare client di chat complessi e altro ancora.

5.2. HTTP RESTful

Adesso eseguiremo una configurazione simile per il servizio RESTful. Il nostro semplice servizio web accetterà una richiesta GET con un nome e risponderà con un saluto.

Usiamo invece il web starter di Spring Boot questa volta:

 org.springframework.boot spring-boot-starter-web 

Ora definiremo un endpoint REST sfruttando il potente supporto per le annotazioni disponibile in primavera:

@RestController @RequestMapping(path = "/rest") public class RestAPIController { @GetMapping(path="/{name}", produces = "application/json") public String getGreeting(@PathVariable("name") String name) { return "{\"greeting\" : \"Hello, " + name + "!\"}"; } }

Infine, creiamo un client in JavaScript:

var request = new XMLHttpRequest() function sendName() { request.open('GET', '//localhost:8080/rest/'+$("#name").val(), true) request.onload = function () { var data = JSON.parse(this.response) showGreeting(data.greeting) } request.send() } function showGreeting(message) { $("#greetings").append("" + message + ""); }

Questo è praticamente tutto! Ancora una volta, c'è una pagina HTML nel repository di codice per lavorare con un'interfaccia utente.

Sebbene profonda nella sua semplicità, la definizione dell'API REST di livello di produzione può essere un'attività molto più ampia!

6. Confronto tra WebSocket e RESTful HTTP

Avendo creato esempi minimi, ma funzionanti, di WebSocket e RESTful HTTP, ora siamo pronti a capire come si comportano l'uno contro l'altro. Esamineremo questo rispetto a diversi criteri nelle prossime sottosezioni.

È importante notare che mentre possiamo confrontare direttamente HTTP e WebSocket poiché sono entrambi protocolli a livello di applicazione, non è naturale confrontare REST con WebSocket . Come abbiamo visto in precedenza, REST è uno stile architettonico che sfrutta HTTP per la comunicazione.

Quindi il nostro confronto con WebSocket riguarderà principalmente le capacità, o la loro mancanza, in HTTP .

6.1. Schema URL

Un URL definisce la posizione univoca di una risorsa Web e il meccanismo per recuperarla . In una comunicazione client-server, il più delle volte cerchiamo di ottenere risorse statiche o dinamiche tramite l'URL associato.

Conosciamo tutti lo schema URL HTTP:

//localhost:8080/rest

Anche lo schema URL di WebSocket non è molto diverso:

ws://localhost:8080/ws

All'inizio, l'unica differenza sembra essere i caratteri prima dei due punti, ma astrae molto ciò che accade sotto il cofano. Esploriamo ulteriormente.

6.2. Stretta di mano

La stretta di mano si riferisce al modo automatico di negoziare il protocollo di comunicazione tra le parti in comunicazione . HTTP è un protocollo senza stato e funziona in un meccanismo di richiesta-risposta. Ad ogni richiesta HTTP, viene stabilita una connessione TCP con il server tramite il socket.

The client then waits until the server responds with the resource or an error. The next request from the client repeats everything as if the previous request never happened:

WebSocket works very differently compared to HTTP and starts with a handshake before actual communication.

Let's see what comprise a WebSocket handshake:

In case of WebSocket, the client initiates a Protocol Handshake request in HTTP and then waits until the server responds accepting an upgrade to WebSocket from HTTP.

Of course, since Protocol Handshake happens over HTTP, it follows the sequence from the previous diagram. But once the connection is established, from there on client and server switches over to WebSocket for further communication.

6.3. Connection

As we saw in the previous subsection, one stark difference between WebSocket and HTTP is that WebSocket works on a persistent TCP connection while HTTP creates a new TCP connection for every request.

Now obviously creating new TCP connection for every request is not very performant and HTTP has not been unaware of this. In fact, as part of HTTP/1.1, persistent connections were introduced to alleviate this shortcoming of HTTP.

Nevertheless, WebSocket has been designed from the ground up to work with persistent TCP connections.

6.4. Communication

The benefit of WebSocket over HTTP is a specific scenario that arises from the fact that the client can server can communicate in ways which were not possible with good old HTTP.

For instance, in HTTP, usually the client sends that request, and then the server responds with requested data. There is no generic way for the server to communicate with the client on its own. Of course, patterns and solutions have been devised to circumvent this like Server-Sent Events (SSE), but these were not completely natural.

With WebSocket, working over persistent TCP communication, it's possible for server and client both to send data independent of each other, and in fact, to many communicating parties! This is referred to as bi-directional communication.

Another interesting feature of WebSocket communication is that it's full-duplex. Now while this term may sound esoteric; it simply means that both server and client can send data simultaneously. Compare this with what happens in HTTP where the server has to wait until it receives the request in full before it can respond with data.

While the benefit of bi-directional and full-duplex communication may not be apparent immediately. we'll see some of the use-cases where they unlock some real power.

6.5. Security

Last but not least, both HTTP and WebSocket leverage the benefits of TLS for security. While HTTP offers https as part of their URL scheme to use this, WebSocket has wss as part of their URL scheme for the same effect.

So the secured version of URLs from the previous subsection should look like:

//localhost:443/rest wss://localhost:443/ws

Securing either a RESTful service or a WebSocket communication is a subject of much depth and can not be covered here. For now, let's just say that both are adequately supported in this regard.

6.6. Performance

We must understand that WebSocket is a stateful protocol where communication happens over a dedicated TCP connection. On the other hand, HTTP is inherently a stateless protocol. This has an impact on how these will perform with the load but that really depends on the use case.

Since communication over WebSocket happens over a reusable TCP connection, the overhead per message is lower compared to HTTP. Hence it can reach higher throughput per server. But there is a limit to which a single server can scale and that is where WebSocket has issues. It's not easy to horizontally scale applications with WebSockets.

This is where HTTP shines. With HTTP each new request can potentially land on any server. This implies that to increase overall throughput we can easily add more servers. This should potentially have no impact on the application running with HTTP.

Obviously an application may itself need state and session stickiness which can make it easier said than done.

7. Where Should We Use Them?

Now, we have seen enough of RESTful service over HTTP and simple communication over WebSocket to form our opinion around them. But where should we use what?

It's important to remember that while WebSocket has emerged out of shortcomings in HTTP, it's not, in fact, a replacement of HTTP. So they both have their place and their uses. Let's quickly understand how can we make a decision.

For the bulk of the scenario where occasional communication is required with the server like getting the record of an employee, it's still sensible to use REST service over HTTP/S. But for newer client-side applications like a stock-price application which requires real-time updates from the server, it's much convenient to leverage WebSocket.

Generalizing, WebSocket is more suitable for cases where a push-based and real-time communication defines the requirement more appropriately. Additionally, WebSocket works well for scenarios where a message needs to be pushed to multiple clients simultaneously. These are the cases where client and server communication over RESTful services will find it difficult if not prohibitive.

Tuttavia, l'uso dei servizi WebSocket e RESTful su HTTP deve essere tratto dai requisiti. Come se non ci fossero proiettili d'argento, non possiamo aspettarci di sceglierne uno per risolvere ogni problema. Quindi, dobbiamo usare la nostra saggezza unita alla conoscenza nella progettazione di un modello di comunicazione efficiente.

8. Conclusione

In questo tutorial, abbiamo esaminato le basi della comunicazione di rete con un'enfasi sui protocolli a livello di applicazione HTTP e WebSocket. Abbiamo visto alcune rapide dimostrazioni di WebSocket e API RESTful su HTTP in Spring Boot.

Infine, abbiamo confrontato le funzionalità dei protocolli HTTP e WebSocket e abbiamo discusso brevemente di quando utilizzarli.

Come sempre, il codice per gli esempi è disponibile su GitHub.