新一代通信协议 - Socket.D
一、簡介
Socket.D 是一種二進制字節流傳輸協議,位于 OSI 模型中的5~6層,底層可以依賴 TCP、UDP、KCP、WebSocket 等傳輸層協議。由 Noear 開發。支持異步流處理。其開發背后的動機是用開銷更少的協議取代超文本傳輸協議(HTTP),HTTP 協議對于許多任務(如微服務通信)來說效率低下。
二、設計目標
- 協議接口豐富,包括 Send, SendAsRequest, SendAndSubscribe,Reply,ReplyEnd
- 支持應用層流量控制
- 支持單連接雙向、多次復用
- 支持斷連后自動重連
- 可以更好的使用 WebSocket 協議
三、消息驅動
網絡通信是異步的,Socket.D 協議包含這一點,并將所有通信建模為在單個網絡連接(TCP)上的、多路復用的消息流,在等待響應時從不同步阻塞。
響應式宣言指出:
反應式系統依賴異步的消息傳遞,從而確保了松耦合、隔離、位置透明的組件之間有著明確邊界。 這一邊界還提供了將失敗作為消息委托出去的手段。 使用顯式的消息傳遞,可以通過在系統中塑造并監視消息流隊列, 并在必要時應用回壓, 從而實現負載管理、 彈性以及流量控制。 使用位置透明的消息傳遞作為通信的手段, 使得跨集群或者在單個主機中使用相同的結構成分和語義來管理失敗成為了可能。 非阻塞的通信使得接收者可以只在活動時才消耗資源, 從而減少系統開銷。
此外,HTTP/2 FAQ 很好地解釋了在持久連接上采用多路復用的面向消息的協議的動機:
-
HTTP/1.x 有一個叫做 “head-of-line blocking(隊頭阻塞)” 的問題,在這種情況下,即在一個連接上一次只能有一個未完成請求。
-
HTTP/1.1 試圖通過流水線(Pipelining)來解決這個問題,但它并沒有完全解決這個問題(大的或慢的響應仍然會阻塞后面的其他響應)。此外,人們發現流水線很難部署,因為許多代理和服務器不能正確地處理它。
在HTTP/1中使用并發連接和域名分片來緩解HOL問題 -
并發連接,瀏覽器針對每個源(域名)可以打開4-8個TCP連接,提升并發度。
-
域名分片,瀏覽器和HTTP/1限制了并發連接的數量,那么就把多個域名指向一臺服務器,從而提升連接數量。
這迫使客戶端使用一些啟發式方法(通常是猜測)來確定什么請求在什么時候放在與源站的哪個連接上;由于加載一個頁面的次數通常是可用連接數量的10倍或者更多。這會導致被阻塞的請求“瀑布式”的增長,從而嚴重的影響性能。
多路復用通過允許多個請求和響應消息在一個連接上同時傳輸來解決這些問題;甚至可以將一條消息的部分與另一條消息的部分混合在一起。
使用HTTP/1,瀏覽器為每個源打開4-8個連接,由于許多站點使用多個源,這可能意味著打開單個頁面要加載30多個TCP連接。
一個應用程序同時打開如此多的連接,打破了TCP所建立的許多假設;由于每個連接都會在響應中傳輸大量的數據,因此TCP緩沖區很大可能會溢出,從而導致擁塞事件和超時重傳。
一個應用程序同時打開如此多的連接,此外,使用如此多的連接不公平地壟斷了網絡資源,“竊取”了其他性能更好的應用程序(如VoIP)的資源。
四、協議交互模型
不合適的協議會增加系統開發的成本。它可能是一個不匹配的抽象,但是我們必須將系統設計強加到他允許的交互模型中。這迫使開發人員花費額外的時間來解決它的缺點,以處理錯誤并獲得可接受的性能。在多語言環境中,這個問題被放大了,因為不同的語言將使用不同的方法來解決這個問題,這需要團隊之間的額外協調。到目前為止,通信協議事實上的標準是HTTP,它只支持請求/響應的交互模式。在某些情況下,這可能不是最理想通信模型。
- Send(只管發送,不要結果)
發完就不管是請求/響應的優化,在不需要響應時很有用。它允許顯著的性能優化,不僅僅是通過跳過響應來節省網絡使用,而且還可以減少客戶端和服務器的處理時間,因為客戶端不需要記錄和等待請求關聯的響應和取消請求。
此交互模型對于支持有損的用例非常有用,例如非關鍵事件日志記錄、或者設備信息上報。
像這樣使用:
clientSession.send("/demo", entity);
- SendAsRquest(發送一個請求,并要求一個響應)
仍然支持標準請求/響應語義,并且仍有望代表 Socket.D 連接上的大多數請求。這些請求/響應交互可以被認為是優化的“只有 1 個響應的流”,并且是在單個連接上多路復用的異步消息。
消費者“等待”響應消息,所以它看起來像一個典型的請求/響應,但它從不同步阻塞。
就像 http 一樣使用:
//同步等待
let response = clientSession.sendAndRequest("/demo", entity).await();
//異步回調
clientSession.sendAndRequest("/demo", entity).thenReply(response => {
});
- SendAndSubscribe(發送一個訂閱,可以接收多個答復)
從一個請求一個響應那兒延伸出來的是多個響應,它允許多條消息流回。將此視為“集合”或“列表”響應,但不是將所有數據作為單個響應返回,而是按順序流回每個元素。
用例可能包括以下內容:獲取視頻列表,在目錄中獲取產品,逐行檢索文件。
可能有點像 mq,還可能通過 range 指定數據區間(或者不指定):
//異步回調
clientSession.sendAndSubscribe("/demo", entity.range(0,5)).thenReply(reply => {
if(reply.isEnd()){
//如果需要識別最后一個?
}else{
//
}
});
五、協議形式
- 連接上傳輸的數據可稱之為流,每個消息都會有一個 sid(流Id)
- 流(Stream)由幀(Frame)組成
- 幀(Frame)包含了流Id(Sid)、事件(Event)、元數據字符串(MetaString)及數據(Data)
幀碼結構為:
[len:int][flag:int][sid:str(<64)][\n][event:str(<512)][\n][metaString:str(<4k)][\n][data:byte(<16m)]
Socket.D 是一個二進制協議,也就是說在一個 Socket.D 連接上傳輸的消息體對數據格式沒有任何要求,應用程序可以為所欲為的壓縮數據量的大小。
這樣的二進制協議通常來說能給性能帶來極大的提升,但是產生的代價是,網絡中間件也會因為無法解讀消息體中的數據,喪失了在對具體應用流量進行監控,日志和路由的能力。Socket.D 通過把每個消息體分成 metaString 和 data 的方式,在保證高效傳輸的前提下,提供了暴露少量元數據給網絡中間件的能力。
- data 一般作為應用本身需要傳遞的業務數據,采取自定義的高效序列化方式,且對網絡基礎設施不可見。
- metaString 采用標準的 urlQueryString 格式。在分布式傳輸的過程中,這些中間件可以按需求對 metaString 進行讀寫,然后監控應用健康狀況或者調整路由。
Socket.D 有哪些適用的場景?
- 移動設備與服務器的連接
- 數據雙向傳輸,且支持流量控制。
- 支持連接修復,比如手機進地鐵之后,網絡斷開一段時間,其他協議需要重新建立連接,Socket.D 會自動修復連接。
- 微服務場景
總結
以上是生活随笔為你收集整理的新一代通信协议 - Socket.D的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【OpenVINO 】在 MacOS 上
- 下一篇: 深入剖析 Linux Cgroups 子