簡單的搭建乙個高併發低時延系統

2021-06-06 20:56:00 字數 3028 閱讀 2339

首先宣告一點:這裡的「高併發」是相對的,相對於硬體而言,而不是絕對的高併發。後者需要分布式來實現,這裡不做討論。本文關注的是單機的高併發。

系統的另乙個指標是呼叫時延和語音時延。這是這個系統的關鍵。最終我們的系統拿到使用者現場測試的時候,效果可能有點太好,對方測試不大相信。其實降低時延只要幾個地方把握好了,應該問題不大的。這裡總結一下。

1、 整體結構:

整體上採用控制與承載相分離的結構。控制部分負責流程的控制部分,包括流程的建立,處理,語音資源的管理等,是系統核心部分。承載部分主要負責語音處理,包括語音編譯碼,加解密,**錄音等。這樣的好處是:1)降低系統的整體複雜度。2)提高系統的可擴充套件性。特別是如果使用者數上去,這種結構更好擴充套件。

這在通訊中其實就是乙個典型的軟交換結構。兩台伺服器,一台負責控制,一台負責承載。控制和承載之間通過網路通訊。

控制程式是乙個程序,可以管理多個承載程式。

2、 流程:

要降低時延,關鍵的一點是功能實現流程的設計。要減少不必要的環節和網元間的互動。資料能夠一次通知就不要兩次互動。必要的時候,為了時延,可以犧牲一點協議的標準性,使用私有協議完成(至少從目前看沒有問題,這個系統是乙個端對端封閉的系統。)。

3、開發語言:

控制層面使用的python來實現的。控制部分流程邏輯複雜,而python很擅長描述邏輯。本來有點擔心python的執行效率,其實沒有必要:整個系統的壓力在承載,而不在控制部分,控制部分不會有太多的壓力;另外,cpu夠強悍,時延的瓶頸在i/o。況且,python也重用了我們之前用c實現的協議編譯碼庫。

承載部分用c來實現。

4、 利用多核

利用多程序來利用多核。在承載伺服器上,並行跑了兩個程序,每個分別處理500路通話。也許執行緒切換成本更低,但是程式設計複雜度高。對於執行緒,我只用最簡單的模型。

控制部分沒有多程序,似乎利用不能這個伺服器的多核,不過目前來看,還不需要,因為現在就能很好的滿足需求。

5、 網路通訊

承載伺服器的壓力中很大一部分來自於網路通訊。按照我們的功能,1000路語音併發,意味著沒20毫秒至少要處理1000個語音包(最惡劣的情況是2000個語音包,包括收發)。

libevent開源,號稱輕量級高效能,而且應用也廣泛,也許是個不錯的選擇。不過在我看來還是有些龐大,很多特性(跨平台,多種通訊模型)我都用不上。

更為關鍵的一點事,linux的epoll介面足夠簡單,而且非常好用。介面中提供乙個引數可以設定使用者資料,這樣我可以把一些資料報括函式指標放進去,從而很方便的構造乙個事件驅動的網路模組。它能夠保證**足夠簡單。

6、 檔案讀寫

整個系統涉及到檔案讀寫的主要有兩塊:錄音及日誌。我們常用的檔案操作介面都是阻塞式,程序(執行緒)會被掛起,等待讀寫完畢,然後在繼續執行。我們都知道,對磁碟的操作要慢很多,所以這個地方是請求時延的乙個瓶頸。

非同步io可以解決這個問題。參考資料:不過網上看到有人說aio介面有bug。時間不多,沒有時間深入研究,還是保守的放棄了這個思路。新技術有風險,使用需謹慎。

libeio也應該是乙個選擇。參考資料:它是用執行緒池來模擬非同步io。問題是,我們的程式主要是寫檔案,而且一般不需要知道結果,在這種情況下使用libeio的必要性有多大?

我們最終的方案是參考libeio,直接為承載程序申請了乙個執行緒來負責寫檔案。主線程負責語音編譯碼及**,完全非阻塞,以保證低延遲。涉及的檔案寫操作,通過介面傳送給另外乙個執行緒呼叫阻塞io介面來實現。執行緒間介面很簡單,一塊要寫的內容加乙個路徑名。

7、 資料庫操作

我們的資料庫使用的是mysql。和檔案讀寫一樣,資料庫操作也是請求時延的乙個瓶頸。在整個流程中,我們會多次的讀寫資料。我們的做法是:系統啟動後,將執行時用到的資料全部讀到記憶體,後面直接檢視記憶體。好在資料不大,這個工作也簡單。如果涉及到資料的新增修改刪除。則另外乙個執行緒完成相關操作,再通知主線程更新記憶體。

最終的結果是,主線程是完全非阻塞的,涉及到阻塞的操作,全部移到另外乙個執行緒中。兩個執行緒不共享任何全域性資料,只通過fifo互動。

這個地方redis也許是可以考慮的一種選擇,它的資料儲存在記憶體,讀寫效率也非常好。不過相對來說還是有點複雜,而且還是nosql,我們的開發人員並不熟悉。「最小驚訝原則」不但適用於程式介面,也適用於系統。

經過所有的這些考慮和優化,可以基本達到目標,而且足夠簡單。

如何榨乾伺服器:

經過上面的一些優化,基本上可以滿足使用者的需求了。但我知道,還沒有完全的利用伺服器的能力(包括cpu,io)。要進一步榨乾伺服器的能力,可以在承載伺服器上將每個程序的處理能力擴大一倍,每個程序處理1000路。也可以考慮再多跑幾個程序。

控**務器沒有充分的利用多核,可以考慮在控**務器也執行兩個承載程式。

這樣下來,初步估計硬體不提高的情況下,註冊使用者數至少能夠提高到6w,併發呼叫數目至少能夠提高到3k。

提高絕對容量和併發:

業務特點不同,通訊行業高併發的解決方案和網際網路行業可能會有一些區別。可以想一下我們使用的**系統,就是乙個例項。它通過分布在全國各地的乙個個使用者歸屬的局端,再配合強大的路由能力,以及端到端之間非常標準的協議來解決。

採用類似的方案也可以提高本系統的容量和併發,不過,目前系統的容量已經可以滿足我們公司市場幾年內的需求,沒有進一步提公升的必要,還是保持簡單的好。

總結:

很多時候,使用開源軟體都是乙個非常不錯的主意,可以避免「重**明輪子」。而且,它還有一定的**:它可以為你的履歷加分。

但是有的時候,你需要的可能並不是「輪子」,想想為乙個滑板安裝乙個汽車輪是什麼效果。

什麼樣的方案才是好的方案?

1、 滿足現在的需求及未來50%的需求。當乙個可預見的需求發生概率超過一半時,為它考慮可擴充套件時必要的。否則會過度設計而衝擊簡單性。

2、 保持簡單。

最後,再次提一下kiss原則,keep it ****** and stupid !

設計乙個高併發系統

公升級過程為 最初系統 新增負載均衡 資料庫分庫分表 讀寫分離 快取集群 訊息中介軟體集群 假設系統機器是4核8g,資料庫伺服器是16核32g。日活使用者1w,系統層面每秒10次請求,資料庫層每秒30次請求。使用者量增長了50倍,日活使用者50萬,高峰期對系統每秒請求500 s,對資料庫的每秒請求1...

如何設計乙個高併發系統

總結如果你確實有真才實學,在網際網路公司裡幹過高併發系統,那你確實拿 offer 基本如探囊取物,沒啥問題。面試官也絕對不會這樣來問你,否則他就是蠢。假設你在某知名電商公司幹過高併發系統,使用者上億,一天流量幾十億,高峰期併發量上萬,甚至是十萬。那麼人家一定會仔細盤問你的系統架構,你們系統啥架構?怎...

如何設計乙個高併發系統

系統拆分,將乙個系統拆分為多個子系統,用dubbo來搞。然後每個系統連乙個資料庫,這樣本來就乙個庫,現在多個資料庫,不也可以抗高併發麼。快取,必須得用快取。大部分的高併發場景,都是讀多寫少,那你完全可以在資料庫和快取裡都寫乙份,然後讀的時候大量走快取不就得了。畢竟人家redis輕輕鬆鬆單機幾萬的併發...