應用層協議設計有感

2022-07-04 22:30:13 字數 2665 閱讀 5605

前些天的時候,一位同事問了一些redis的網路協議相關的問題,然後交流中談了一些我的想法,又參考了一些資料,記錄下來。

我們在設計乙個應用層網路協議是,我們需要關注哪些方面? 或者說乙個好的應用層協議應該有哪些屬性?

好的應用層協議是可伸縮的。一些應用層協議比如http,會建立幾條並行的鏈結的到服務端,這樣做可以減少延遲,增加吞吐量,但是在傳輸層和服務端看來,這些鏈結相互之間是沒有關聯關係的。而且可能會造成額外的一些問題。

在傳輸層,比如tcp協議採用自適應演算法根據網路的條件進行高效的資料傳輸。這個自適應的過程是在每個鏈結上進行的。如果同時有多個鏈結到服務端,那也就是需要針對每個鏈結進行自適應的調節過程。這就在網路中引入了額外的不必要的壓力,而且tcp如果用了慢啟動擁塞演算法,那麼,應用層序還是可以感受到這種額外的延遲。如果客戶端的頻寬不足,這種問題反而會有可能變的更明顯。

在服務端,每個從客戶端建立的鏈結有可能都要經過認證,因此,服務端的負載會隨著遠大於實際使用者數的鏈結的增加而增大。更嚴重的情況是,如果有乙個使用者發起了非常多的鏈結,服務端的效能可能會嚴重下降,甚至會導致無法為其他使用者提供服務。

http協議1.0版本,提供了持久鏈結機制(keep-alive),允許乙個鏈結在處理完事務後可以不用關閉鏈結,以便為將來的http請求復用現存的鏈結。這種機制可以避開緩慢的鏈結建立階段,而且還可以避免慢啟動的擁塞適應階段,進行更快速的資料傳輸。但是管理持久鏈結時要特別小心,防止積累出大量的空閒鏈結,耗費本地以及遠端的資源。並行鏈結和持久鏈結配合使用可能是最高效的方式。

類似http的協議一般都是只允許客戶端傳送請求到服務端,而不允許服務端直接傳送資料到客戶端。當想要完成服務端推送一些訊息到客戶端之類的業務需求時,只能讓客戶端不斷的向服務端傳送輪詢請求。這樣會導致浪費網路資源,也會對服務端的資源造成浪費。所以後來引入了wesocket,可以讓http鏈結轉換為一般的鏈結,就可以避開http協議的要求了。

另外就是類似http的協議,一般都是乙個請求傳送過去,然後等待乙個回應完成,然後再發下乙個請求,這種序列化處理會因為網路往返時延導致延遲較高。所以一般會用到pipeline技術。也就是一次傳送多個請求,要求服務端按照相應的順序傳送回來。 這裡面的重中之重就是服務端必須要確保傳送的順序是請求的順序。還有就是如果客戶端傳送了10個請求,但是服務端在處理完5個請求後發生了錯誤就關閉了鏈結,那麼客戶端就要確保能夠重新傳送剩餘的5個請求,這帶來了一定的實現複雜度。

但是類似http的協議,在我看來還有另外乙個好處。因為只允許客戶端到服務端傳送請求,那麼會讓c/s雙方的業務變得調理清晰。比如說,業務服務由客戶端請求觸發,專門有另外乙個鏈結提供服務端推送資料到客戶端。這種職責單一性會帶來更好的可維護性。而且這種職責單一性,可以很容易的用其他語言實現,也可以採用同步阻塞io或者非阻塞io甚至非同步io機制實現。假設如果乙個鏈結即處理業務請求,又能夠讓服務端推送資料,那麼更好的實現模型是event-driven,如果用同步阻塞io實現,就會變得異常複雜。在我最近的接觸中,發現redis的協議就是此種設計。redis的協議裡大部分都是客戶端發起請求,服務端處理後返回結果; 但是有乙個特殊的請求,就是subscribe,客戶端發起這個命令後,如果再通過這個鏈結傳送資料到服務端,服務端會直接返回錯誤。處於subscribe狀態的鏈結只能等著服務端主動推送來的資料。 

可伸縮性的另外乙個方面就是服務端和客戶端的部署關係。功能實現在服務端總是比實現在客戶端能夠帶來更多的可伸縮性,畢竟大多數情況下,服務端公升級總是要比客戶端容易一些。

好的應用層協議是高效的。我們大多數都會非常關心傳輸的效率,總是認為傳輸速度越快越好。一般的做法是批量請求,乙個請求中包含了很多要一起處理的事情;或者重疊衝球,比如http pipeline機制和imap中針對請求打標機的方式,讓客戶端無需等待回應就可以繼續向服務端傳送請求,然後在接收時可以通過客戶端的狀態機處理回應資料,這樣就能夠減少等待時間。有些時候,效能可能會和擴充套件性產生衝突,這個時候就需要自己根據實際場景和需求做權衡。

好的應用層協議是簡單的。經驗法則是如果乙個協議設計的很差,那麼在做一些簡單的事情時反而變成了挑戰。正確的方式是讓簡單的事情簡單的完成,複雜的事情通過複雜的方式完成。另外一條經驗法則是,如果應用層協議有兩種方式完成同樣的事情,那麼可能在基礎協議設計架構上存在問題。簡單並不意味著想法簡單,而是說,簡單是一種精心設計的,可以解決包括了問題域中的任何事情,甚至是邊界的更複雜的問題。一致性是會讓設計變得優雅和簡單。

好的應用層協議是可擴充套件的。我們程式設計師經常會遇到需求的變更,或者一些策略變動較多的問題。這也就意味著,協議本身是逐步演進的。所以乙個可擴充套件的協議對我們來說非常的重要。可擴充套件的協議可以方便的向前或者向後相容,比如protobuff,json,http等協議。

好的應用層協議是人類可讀的。可讀性要根據自己實際場景和需要進行權衡。但是在我個人看來,可讀的協議容易幫助排查問題。想象一下,遇到了乙個網路問題,需要抓包分析,滿眼的二進位製流是什麼感受?再想象下,如果是http協議,你還有這種困難麼?

好的應用層協議是健壯的。postel的健壯性法則「be conservative in what you send, be liberal in what you accept.(傳送時保守,接手時開放)」講究的更多的是吞掉對方的錯誤,但是如果對接收到的資料進行檢查並返回了錯誤,那麼對對方可能會有更多的好處。假設跨語言實現的時候或者多版本客戶端的時候,如果沒有乙個一致的錯誤檢查,會讓人很蛋疼的吧。

《unix程式設計藝術》

應用層協議

應用層協議定義了執行在不同端系統上的應用程式程序如何相互傳遞訊息。特別是定義了 交換的訊息型別,如請求訊息和響應訊息。各種訊息型別的語法,如訊息中的各個字段及其詳細描述。欄位的語義,即包含在字段中的資訊的含義。程序何時 如何傳送訊息及對訊息進行響應的規則。有些應用層協議是由rfc文件定義的,因此它們...

應用層協議

dns 網域名稱解析協議 http 超文字傳輸協議 ftp 文字傳輸協議 tlent internet遠端登入服務的標準協議 smtp 簡單郵件傳輸協議 snmp 簡單網路管理協議 ssh 協議 加密的安全的連線 ftp 給予tcp文字傳輸的協議 tftp 基於udp,簡單檔案傳輸協議 1.網域名稱...

應用層協議 HTTP協議

認識url 我們平時說的 其實就是說的url。http請求 無狀態 並不會記錄當前使用者在訪問。https 加密協議 http常見header 分離報頭和有效載荷 正文 空行分離http的方法方法 說明支援的http協議版本 get 私密性不好 獲取資源 1.0 1.1 post 正文傳參 傳輸實體...