1. 程式人生 > >Java架構-跨程序通訊,到底用長連線還是短連線

Java架構-跨程序通訊,到底用長連線還是短連線

一個完整的軟體系統大多數情況下是由多個程序共同協作進行的,哪怕它們在同一臺伺服器上。所以,程序之間如何進行高效的通訊至關重要。

單個應用程式 + 單個數據庫這套基礎開發套餐我相信每個人都經歷過,甚至在初期它們還有可能部署在同一臺伺服器上。既然應用程式和資料庫分屬於兩個不同的程序,所以這個問題本質上還是兩個程序之間的通訊問題。

兩個程序之間如果要通訊,很顯然必須要建立一個連線,通過它來相互傳輸資料。原則上,如果兩個程序在同一臺伺服器上,有很多種方式可以進行相互通訊。不過在分散式系統中,不同的程序很多時候被部署在不同的伺服器上。所以我們這次只聊基於 TCP/IP 的通訊方式,因為對大家來說這是最普遍會用到的方式,不管是應用程式間的遠端呼叫(RPC)還是應用程式與資料庫間的呼叫(DAL),皆是如此。

通過套接字(socket)建立連線

socket 與 TCP/IP 之間是脣齒相依般的關係,聯絡緊密,先來看下維基百科對 socket 的定義。

  • socket 是計算機網路中用於在節點內傳送或接收資料的內部端點。具體來說,它是網路軟體 (協議棧) 中這個端點的一種表示,包含通訊協議、目標地址、狀態等,是系統資源的一種形式。

它在網路中所處的位置大致就是下面的黑色部分,應用層與傳輸層之間。

其中的傳輸層就是 TCP/IP 所在的地方,而你平時通過程式碼編寫的應用程式大多屬於應用層範疇,socket 在這裡起到就是連線應用層與傳輸層的作用。

socket 的誕生是為了應用程式能夠更方便的將資料經由傳輸層來傳輸,所以它本質上就是對 TCP/IP 的運用進行了一層封裝,然後應用程式直接呼叫 socket API 即可進行通訊。那麼它是如何工作的呢?它分為 2 個部分,服務端需要建立 socket 來監聽指定的地址,然後等待客戶端來連線。而客戶端則需要建立 socket 並與服務端的 socket 地址進行連線。

這圖展示的就是建立 TCP/IP 連線的過程,經典的叫法為“三次握手”的過程。顧名思義,這個過程中來回產生了三次網路通訊。

接下來的資料傳輸過程就簡單很多,傳送資料就是客戶端往服務端通訊,服務端處理完之後的資料返回則相反。

值得注意的是,傳輸的過程涉及到資料 Copy,不過這些 Copy 是必不可少的。其中的傳送緩衝區和接收緩衝區就是套接字快取 (socket buffer)。

連線使用完之後需要關閉,不過 TCP/IP 連線關閉過程比建立更復雜一些,次數多了一次,這就是經典的“四次握手”過程。

簡單總結一下 socket。socket 是程序間資料傳輸的媒介,為了保證連線的可靠,你需要特別注意建立連線和關閉連線的過程。為了確保準確、完整的資料傳輸,客戶端和服務端來回進行了多次網路通訊才得以完成連線的建立和關閉,這同時也是你在運用一個連線時所花費的額外成本。

基於 socket 我們可以選擇建立長連線或者短連線,在實際運用中兩者都有可能被用到。

長連線和短連線的區別

先帶你來認識一下它倆的區別。

長連線意味著進行一次資料傳輸後,不關閉連線,長期保持連通狀態。如果兩個應用程式之間有新的資料需要傳輸,則直接複用這個連線,無需再建立一個新的連線。就像下圖這樣。

它的優勢是在多次通訊中可以省去連線建立和關閉連線的開銷,並且從總體上來看,進行多次資料傳輸的總耗時更少。缺點是需要花費額外的精力來保持這個連線一直是可用的,因為網路抖動、伺服器故障等都會導致這個連線不可用,甚至是由於防火牆的原因。所以,一般我們會通過下面這幾種方式來做“保活”工作,確保連線在被使用的時候是可用狀態:

利用 TCP 自身的保活(Keepalive)機制來實現,保活機制會定時傳送探測報文來識別對方是否可達。一般的預設定時間隔是 2 小時,你可以根據自己的需要在作業系統層面去調整這個間隔,不管是 Linux 還是 Windows 系統。

上層應用主動的定時傳送一個小資料包作為“心跳”,探測是否能成功送達到另外一端。 保活功能大多數情況下用於服務端探測客戶端的場景,一旦識別客戶端不可達,則斷開連線,緩解服務端壓力。

提前多說一句,如果在做了高可用的分散式系統場景中運用長連線會更麻煩一些。因為高可用必然包含自動故障轉移、故障隔離等機制。這恰恰導致了一旦發生故障,客戶端需要及時發現哪些連線已處於不可用狀態,並進行相應的重連,包括重新做負載均衡等工作。

瞭解完了長連線,那麼短連線就很容易理解了。短連線意味著每一次的資料傳輸都需要建立一個新的連線,用完再馬上關閉它。下次再用的時候重新建立一個新的連線,如此反覆。

它的優勢是由於每次使用的連線都是新建的,所以基本上只要能夠建立連線,資料就大概率能送達到對方。並且哪怕這次傳輸出現異常也不用擔心影響後續新的資料傳輸,因為屆時又是一個新的連線。缺點是每個連線都需要經過三次握手和四次握手的過程,耗時大大增加。

另外,短連線還有一個致命的缺點。我們回到前面提到的維基百科對 socket 的定義,其中說到socket 包含通訊協議、目標地址、狀態等。實際當你在基於 socket 進行開發的時候,這些包含的具體資源主要就是這 5 個:源 IP、源埠、目的 IP、目的埠、協議,有個專業的叫法稱之為“五元組”。在一臺計算機上只要這五元組的值不重複,那麼連線就可以被建立。然而一臺計算機最多隻能開啟 65535 個埠,如果現在兩個程序之間需要通訊,作為服務端的 IP 和埠必然是固定的,因此單個客戶端理論上最多隻能與服務端同時建立 65535 個 socket 連線。如果除去作業系統和其它程序所佔用的埠,實際還會更少。所以,一旦使用不當,在很短的時間內建立了大量連線,埠很容易被佔用完。這不但會導致自身無法正常工作,還會影響到同一臺計算機上的其它程序。

我猜你在專案中大多數情況使用的是短連線的方式,因為這對我們程式設計來說可以少考慮很多問題,潛在的這些缺點可能是你沒有遇到或者意識到而已。存在必有其價值,接下去我們根據實際的案例讓你清楚知道如何來選擇它們。

長連線和短連線的選擇

我想你肯定見過一些監控或者實時報價類系統,比如股票軟體,它需要在幾秒之內重新整理最新的價格。像這種場景中同時包含了需要運用長連線的三個主要因素:高頻、服務端主動推送和有狀態。

  • 高頻的原因我想你根據前面的內容也明白了,因為頻次越高的話,使用短連線帶來的建立連線和關閉連線的總開銷越大。

  • 而服務端主動推送也需要長連線的原因是,由於服務端往往是“中心化”的,一般都是 1 個服務端為多個客戶端提供服務。所以,如果使用短連線的方式,那麼在客戶端未主動連線到服務端的情況下,服務端並不知道需要往哪些客戶端去推送資料,這是原因之一。所以此時,長連線成為了一個很好的選擇。另外一個原因是,哪怕客戶端通過定時的短連線輪詢方式進行主動連線,除了增加了額外的建立連線和關閉連線的開銷外,還可能遇到通訊完成後結果資料並未發生變化,做了無用功。

  • 成熟股票軟體的服務端,為了支撐更多的使用者以及做高可用,必然部署了多臺。但是這個業務場景,使用者無法容忍由於多個服務端之間資料同步的誤差導致他在客戶端看到的價格重新整理產生“回退”現象。所以,只能儘量保持一直連線在同一臺伺服器上,才能避免這個情況。這種場景被稱之為“有狀態”,也可以理解為是“序列”的,因為多次請求的前後需要保持“連續性”。

短連線則更適用於諸如閱讀類軟體的場景中,例如,很多時候使用者點開一篇文章後需要花一些時間進行閱讀,這個時間有長有短,並且直到使用者下一次操作之前都沒有資料傳輸發生。這個場景中包含了運用短連線的兩個主要因素:低頻、無狀態

  • 因為低頻,所以更能容忍建立連線和關閉連線的開銷。

  • 使用者的下一次點選往往跳轉到了其它文章,並且新開啟的與當前文章並不需要具有“連續性”,所以這種場景我們稱之為“無狀態”的。另外,理論上同一時刻開啟幾篇文章也不會存在什麼不妥。

通過這兩個案例我們可以總結出一個決定何時運用長連線和短連線的最佳實踐。

長連線適用於:兩個程序之間需要高頻通訊並且具備服務端主動推送或者有狀態(需序列)兩者之一的場景,否則並不是必選項。

短連線適用於:兩個程序之間通訊頻率較低,或者屬於無狀態(可並行)的場景,否則並不是必選項。

其它情況就根據所需的側重點來,比如側重效能就長連線,側重編碼的便捷性就選擇短連線。

總結

至此,相信你應該清楚了長連線和短連線在跨程序通訊中該如何選擇,而且還對通過 socket 建立 TCP 連線有了一定的認識。你在實際的工作中可能遇到的場景千奇百怪,只需要保持先識別所處場景的特點,再基於這些特點來作出選擇的習慣,必然至少是個不錯的決策。

不過有時候我們可能需要一箇中庸的方案來作為預設選擇,因為很多場景中的請求並不是平穩的,甚至波動會較大,而且可能同時存在有狀態和無狀態的場景,此時如果單方面的選擇長連線或者短連線都會產生較多的資源浪費。那麼我們可以通過增加一些複雜度來實現一個能夠綜合長連線和短連線各自優點的方案:建立多個長連線,每次資料傳輸的時候獨佔使用,用完之後放回,再給後續使用。這種方案被稱之為“連線池”。例如, 很多的資料庫訪問框架都內建了連線池機制,因為作為底層框架的它不知道會被使用到何種場景的系統中,所以提供了這個選項。

連線池的運作流程大致如下圖。除了上面所說的,獨佔使用,用完放回之外,一般都會在應用程式啟動時預先建立好指定資料量的連線,以更好應對冷啟動後請求數快速上升帶來的資源競爭問題,這個數量一般稱之為最小連線數。另外,如果新的請求進來時,所有已建立的連線都在使用中,但是連線數的上限未達到指定數量,可以再建立新的長連線來使用,用完依舊放回到空閒池,相當於把連線池擴大了,這個上限數量一般稱之為最大連線數。

不知道大家對長連線和短連線的運用有什麼樣的經驗和認識呢?歡迎在下方評論區留言,我們一起討論

我本人邀約各大BATJ架構大牛共創Java架構師社群群,(群號:673043639)致力於免費提供Java架構行業交流平臺,通過這個平臺讓大家相互學習成長,提高技術,讓自己的水平進階一個檔次,成功通往Java架構技術大牛或架構師發展。

為什麼某些人會一直比你優秀,是因為他本身就很優秀還一直在持續努力變得更優秀,而你是不是還在滿足於現狀內心在竊喜!

合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!

希望此文能幫到大家的同時,也聽聽大家的觀點。歡迎留言討論,加關注,分享你的高見!持續更新

  • To-陌霖Java架構

分享網際網路最新文章 關注網際網路最新發展