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 標準化,具體定義為 RFC 6455 於 2011 年發佈。 如今,大多數現代 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. Spring Boot 快速搭建
沒有什麼比看到可運行的示例更好了。因此,我們將使用 WebSocket 和 RESTful HTTP 構建簡單的用例,以便更深入地探索它們,然後進行比較。讓我們為兩者創建一個簡單的服務器和客户端組件。
我們將使用 JavaScript 創建一個簡單的客户端,該客户端將發送一個名稱。同時,我們將使用 Java 創建一個服務器,該服務器將用問候語進行響應。
5.1. WebSocket
為了在 Spring Boot 中使用 WebSocket,我們需要 合適的 starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>我們現在將配置 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");
}
}讓我們快速定義一個簡單的 WebSocket 服務器,該服務器接受一個名稱並返回一個問候語:
@Controller
public class WebSocketController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(Message message) throws Exception {
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}最後,讓我們構建客户端與此 WebSocket 服務器進行通信。鑑於我們強調瀏覽器與服務器之間的通信,讓我們用 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>");
}本示例完成了 WebSocket 服務器和客户端的工作演示。代碼倉庫中包含一個 HTML 頁面,提供了一個簡單的用户界面,用於與之交互。
雖然這只是一個初步的介紹,但 WebSocket 與 Spring 可以用於構建複雜的聊天客户端和其他應用。
5.2. RESTful HTTP
我們將現在進行類似的配置,用於 RESTful 服務。 我們的簡單 Web 服務將接受一個 GET 請求,其中包含一個名稱,並以問候語進行響應。
讓我們這次使用 Spring Boot 的 Web Starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>現在,我們將定義一個利用 Spring 中強大的註解支持的 REST 端點:
@RestController
@RequestMapping(path = "/rest")
public class RestAPIController {
@GetMapping(path="/{name}", produces = "application/json")
public String getGreeting(@PathVariable("name") String name)
{
return "{\"greeting\" : \"Hello, " + name + "!\"}";
}
}最後,讓我們創建一個 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>");
}基本上就是這樣! 再次強調,有一個 HTML 頁面存儲在代碼倉庫中,用於與用户界面交互。
儘管其簡潔性令人驚歎,但定義生產級別的 REST API 卻可能是一項非常複雜的工作!
6. WebSocket 與 RESTful HTTP 的比較
鑑於我們已經創建了最小但可運行的 WebSocket 和 RESTful HTTP 示例,現在我們可以瞭解它們之間的差異。我們將從下一部分子章節中考察多個標準。
需要注意的是,雖然我們可以直接比較 HTTP 和 WebSocket,因為它們都是應用層協議,但將 REST 與 WebSocket 進行比較並不自然。正如我們之前所見,REST 是一種架構風格,它利用 HTTP 進行通信。
因此,我們的比較將主要集中在 HTTP 的能力(或缺乏)方面。
6.1 URL 方案
URL 定義了網絡資源的唯一位置以及檢索它的機制。 在客户端-服務器通信中,我們通常通過其關聯的 URL 獲取靜態或動態資源。
我們都熟悉 HTTP URL 方案:
http://localhost:8080/restWebSocket URL 方案與 HTTP 相比並沒有太多不同:
ws://localhost:8080/ws開篇來看,似乎唯一的區別在於冒號之前的字符,但它抽象了大量的底層操作。我們來進一步探索。
6.2. Handshake
Handshake
WebSocket 與 HTTP 相比,工作方式非常不同,它在實際通信之前會進行握手。 以下是 WebSocket 握手所包含的內容: 在 WebSocket 中,https://localhost:443/rest
wss://localhost:443/ws 保障 RESTful 服務或 WebSocket 通信,是一個涉及深度的課題,此處無法詳盡。目前,我們僅説明兩者在相關方面均得到充分支持。 我們必須理解 WebSocket 是一種狀態化協議,通信發生在專用 TCP 連接上。相比之下,HTTP 本質上是一種無狀態協議。這會影響它們在高負載下的性能,但具體取決於用例。 由於 WebSocket 通信發生在可重用 TCP 連接上,因此每條消息的開銷低於 HTTP。因此,它可以達到更高的吞吐量。但單個服務器可以擴展的上限是 WebSocket 的問題。難以使用 WebSockets 水平擴展應用程序。 HTTP 在這裏表現出色。使用 HTTP,每個新請求都可能到達任何服務器。這意味着為了提高整體吞吐量,我們可以輕鬆地添加更多服務器。這可能不會對使用 HTTP 的應用程序產生影響。 當然,應用程序本身可能需要狀態和會話粘性,這可能會比説起來容易做起來難。 現在,我們已經充分了解了通過 HTTP 進行的 RESTful 服務以及通過 WebSocket 進行的簡單通信,從而對它們有了自己的看法。但我們應該在什麼情況下使用它們呢? 重要的是要記住,雖然 WebSocket 已經從 HTTP 的不足之處中發展而來,但它實際上並不是 HTTP 的替代品。因此,它們都有各自的位置和用途。讓我們快速瞭解如何做出決定。 對於大多數場景,當需要與服務器進行偶爾的通信,例如獲取員工記錄時,仍然明智地使用 HTTP/S 上的 RESTful 服務。但是,對於像股票價格應用程序這樣的新客户端應用程序,這些應用程序需要從服務器獲取實時更新,利用 WebSocket 更加方便。 總的來説,WebSocket 更適合在推送到客户端的基於實時通信的需求定義更合適的情況下。此外,WebSocket 在需要同時向多個客户端推送消息的場景中也表現良好。這些是客户端和服務器通過 RESTful 服務難以甚至無法實現的場景。 儘管如此,WebSocket 和 HTTP 上的 RESTful 服務的使用必須基於需求。就像沒有萬能的解決方案一樣,我們不能只是選擇一個來解決所有問題。因此,我們必須結合我們的智慧和知識來設計高效的通信模型。 在本教程中,我們回顧了網絡通信的基礎知識,重點介紹了應用層協議 HTTP 和 WebSocket。我們還演示了 WebSocket 和 HTTP 上的 RESTful API 在 Spring Boot 中的一些快速示例。 最後,我們比較了 HTTP 和 WebSocket 協議的功能,並簡要討論了何時使用每種協議。
6.6. 性能
7. 應該在什麼情況下使用它們?
8. 結論