1. 概述
在本教程中,我們將探討客户端-服務器通信的基礎知識,並通過兩種流行的選項進行探索。我們將看到 WebSocket(作為一種新興技術)的表現如何,以及它與更流行的 RESTful HTTP 的對比。
2. 網絡通信的基礎
在深入探討不同選項及其優缺點之前,我們先快速回顧一下網絡通信的概況。這將有助於我們更好地理解相關內容。
可以通過開放系統互連(OSI)模型來理解網絡通信。
OSI模型將通信系統劃分為七層抽象層:
該模型的頂層是應用層,這是本教程中的重點。但是,在比較 WebSocket 和 RESTful HTTP 的過程中,我們還會討論頂部的四層的一些方面。
應用層是離用户最近的一層,負責與參與通信的應用程序進行交互。常用的協議包括 FTP、SMTP、SNMP、HTTP 和 WebSocket。
3. WebSocket 和 RESTful HTTP 的描述
雖然通信可以發生在任意數量的系統之間,但我們特別感興趣的是客户端與服務器之間的通信。更具體地説,我們將重點關注瀏覽器與 Web 服務器之間的通信。這將是我們比較 WebSocket 與 RESTful HTTP 的框架。
但在我們繼續之前,為什麼不快速瞭解它們是什麼!
3.1. WebSockets
按照正式定義,《WebSocket》是一種雙向、全雙工通信協議,它在持久的 TCP 連接上運行。我們將逐步詳細瞭解這一聲明中的每個部分。
WebSocket 協議由 IETF 標準化,並在 2011 年通過 RFC 6455。 如今大多數現代 Web 瀏覽器都支持 WebSocket 協議。
3.2. RESTful HTTP
雖然我們都熟悉 HTTP,因為它在互聯網上的無處不在,它也是一種應用層通信協議。 HTTP 是一種基於請求-響應的協議,我們稍後將更詳細地瞭解它。
REST(表述性狀態轉移)是一種對 HTTP 施加一組約束的架構風格,用於創建 Web 服務。
4. WebSocket 子協議
WebSocket 定義了客户端與服務器之間雙向通信的協議,它不對交換的消息進行任何限制。 這留給通信各方在子協議協商中達成一致。
對於非簡單的應用程序,開發子協議並不方便。 幸運的是,有許多流行的子協議可用,例如 STOMP。
STOMP 代表 Simple Text Oriented Messaging Protocol,它在 WebSocket 上工作。 Spring Boot 對 STOMP 具有第一類支持,我們將在此教程中使用它。
5. Quick Setup in Spring Boot
There’s nothing better than seeing a working example. So, we’ll build simple use-cases in both WebSocket and RESTful HTTP to explore them further and then compare them. Let’s create a simple server and client component for both.
We’ll create a simple client using JavaScript which will send a name. And, we’ll create a server using Java which will respond with a greeting.
5.1. WebSocket
To use WebSocket in Spring Boot, we’ll need the appropriate starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
We’ll now configure the STOMP endpoints:
@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");
}
}
Let’s quickly define a simple WebSocket server which accepts a name and responds with a greeting:
@Controller
public class WebSocketController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(Message message) throws Exception {
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
Finally, let’s build the client to communicate with this WebSocket server. As we are emphasizing browser-to-server communication, let’s create a 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("<tr><td>" + message + "</td></tr>");
}
This completes our working example of a WebSocket server and client. There is an HTML page in the code repository which provides a simple user interface to interact with.
While this just scratches the surface, WebSocket with Spring can be used to build complex chat clients and more.
5.1. WebSocket
To use WebSocket in Spring Boot, we’ll need the appropriate starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
We’ll now configure the STOMP endpoints:
@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");
}
}
Let’s quickly define a simple WebSocket server which accepts a name and responds with a greeting:
@Controller
public class WebSocketController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(Message message) throws Exception {
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
Finally, let’s build the client to communicate with this WebSocket server. As we are emphasizing browser-to-server communication, let’s create a 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("<tr><td>" + message + "</td></tr>");
}
This completes our working example of a WebSocket server and client. There is an HTML page in the code repository which provides a simple user interface to interact with.
While this just scratches the surface, WebSocket with Spring can be used to build complex chat clients and more.
5.2. RESTful HTTP
We’ll go through a similar set-up for RESTful service now. Our simple web service will accept a GET request with a name and responds with a greeting.
Let’s use Spring Boot’s web starter instead this time:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Now, we’ll define a REST endpoint leveraging powerful annotation support available in Spring:
@RestController
@RequestMapping(path = "/rest")
public class RestAPIController {
@GetMapping(path="/{name}", produces = "application/json")
public String getGreeting(@PathVariable("name") String name)
{
return "{\"greeting\" : \"Hello, " + name + "!\"}";
}
}
Finally, let’s create a client in JavaScript:
var request = new XMLHttpRequest()
function sendName() {
request.open('GET', 'http://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("<tr><td>" + message + "</td></tr>");
}
That’s pretty much it! Again, there’s an HTML page in code repository to work with a user interface.
Although profound in its simplicity, defining production grade REST API can be much more extensive task!
6. 比較 WebSocket 和 RESTful HTTP
已經創建了最小但可工作的 WebSocket 和 RESTful HTTP 的示例,我們現在準備好理解它們如何相互比較。我們將通過下一組子部分來研究這些內容。
重要的是要注意,雖然我們可以直接比較 HTTP 和 WebSocket,因為它們都是應用程序層協議,
6.1. URL 方案
URL http://localhost:8080/rest
WebSocket URL 方案與 HTTP 方案不太不同: 起初,唯一不同之處似乎只是前綴中的字符,但它抽象了許多在底層發生的事件。讓我們進一步探索。
WebSocket 與 HTTP 運作方式非常不同,在實際通信之前會進行 handshake。 讓我們看看 WebSocket handshake 的構成: 在 WebSocket 的情況下,6.3. 連接
正如我們在上一節中看到的,WebSocket 和 HTTP 之間的一個主要區別在於 WebSocket 在持久 TCP 連接上工作,而 HTTP 僅創建新的 TCP 連接用於每個請求。 現在,顯然對於每個請求創建新的 TCP 連接並不高效,HTTP 並沒有對此不敏感。事實上,作為 HTTP/1.1 的一部分,持久連接已被引入以緩解 HTTP 的這一缺陷。 儘管如此,6.4. 通信
WebSocket 與 HTTP 的優勢在於一個在舊式 HTTP 中不可能出現的場景,即客户端可以服務器可以以一種方式進行通信,這在舊式 HTTP 中是不可能的。當然,已經設計出一些模式和解決方案來規避這種問題,例如服務器端事件(SSE),但這些並非自然。 通過 WebSocket,在持久 TCP 通信中,現在,雖然這個術語聽起來有些抽象,但它只是意味着 6.5. 安全性
最後但並非最不重要的一點是,https://localhost:443/rest
wss://localhost:443/ws
無論安全 RESTful 服務還是 WebSocket 通信,都具有很大的深度,這裏無法涵蓋。現在,我們只是説兩者都得到了充分的支持。 我們必須理解,WebSocket 是一種狀態協議,通信發生在持久的 TCP 連接上。另一方面,HTTP 本質上是一個無狀態協議。這會影響這些內容與負載的性能,但實際上取決於使用情況。 7. 我們應該如何使用它們?
現在,我們已經充分了解了通過 HTTP 進行的 RESTful 服務以及通過 WebSocket 進行的簡單通信,從而對它們形成了一定的看法。但我們應該如何使用它們呢? 重要的是要記住,雖然 WebSocket 已經從 HTTP 的不足中發展而來,但它實際上並不是 HTTP 的替代品。因此,它們都有各自的位置和用途。讓我們快速瞭解如何做出決定。 對於大多數場景,當需要與服務器進行偶爾的通信,例如獲取員工記錄時,仍然明智地使用 RESTful 服務(通過 HTTP/S)。 但對於新的客户端應用程序,例如股票價格應用程序,這些應用程序需要從服務器獲取實時更新,使用 WebSocket 更加方便。 總的來説,WebSocket 更適合在推送到客户端的實時通信定義需求的情況下使用。此外,WebSocket 在需要同時向多個客户端推送消息的場景中表現良好。這些是客户端和服務器通過 RESTful 服務進行通信,如果不是困難,甚至是不切實際的情況。 儘管如此,WebSocket 和 RESTful 服務(通過 HTTP)的使用必須基於需求。就像沒有萬能的解決方案一樣,我們不能僅僅期望選擇一個來解決所有問題。因此,我們必須結合我們的智慧和知識來設計高效的通信模型。 在本教程中,我們回顧了網絡通信的基礎知識,重點介紹了應用層協議 HTTP 和 WebSocket。我們還演示了 WebSocket 和 RESTful API 在 Spring Boot 中的一些快速示例。 最後,我們比較了 HTTP 和 WebSocket 協議的特性,並簡要討論了何時使用每種協議。ws://localhost:8080/ws6.2. 手動握手

6.6. 性能
8. 結論