muduo網絡庫設計總結


本篇結束muduo網絡庫部分學習的筆記,總結一下muduo網絡庫的模塊組成,同時會提供筆記中個模塊的實現代碼,這些模塊代碼單獨抽出同時去除了muduo中對boost的依賴,改用c++11中的組件或者用單獨的類替換,會使得muduo的各個組件會更爲簡潔易學。
基於C++11的muduo :https://github.com/BethlyRoseDaisley/SimpleMuduo 代碼可能還有些bug和不完善的地方,簡單使用和學習的話完全夠用,也可自行完善。

muduo網絡庫簡介

muduo是一個高質量的事件驅動型的網絡庫,其核心代碼不超過4500行,使用的non-blocking IO(IO multiplexing)+ one loop per
thread模型。此模型每個IO線程裏面只有一個事件循環(即一個Reactor),處理讀寫和定時事件,激活的事件通過回調方式提供用戶處理業務邏輯。
在linux下的話,可以把事件當做一個文件描述符,換句話也就是說一個file descriptor只能由一個線程讀寫。
一個線程最多隻有一個EventLoop,而EventLoop中的循環即是在不停的監視這些描述符,當描述符可讀或可寫的時候,通過回調函數提供給用戶處理。
這樣我們可以很方便地把不同的socket套接字的描述符放到不同的線程去, 也可以把一些socket放到一個線程裏,這樣這些socket就是線程安全的,因爲始終只有EventLoo所在線程在讀寫它們,極大的降低了我們的編程複雜性。

使用起來的話,它對外看上去應該這個樣子,EventLoop一直處在事件循環中,通過IO複用機制select/poll/epoll回調激活的事件。

muduo網絡庫模塊組成

muduo的組件大致可劃分爲 5個部分, Reactor、TimerQueue和Eventfd、Acceptor和Connector、TcpConnection、TcpServer和TcpClient。
muduo網絡部分的簡化類圖

如果只注重服務端的話,可以TcpClient省去,Poller在muduo中是個純虛基類,現在用poll(2)具體化它,省略它們後的結構應該是這樣的。
EventLoop和Poller及Channel組成Reactor部分、Acceptor作爲TcpServer的監聽器、TcpConnection負責處理socket的讀寫等事件、而TcpServer處理TcpConnection讀寫完成後的回調事件。

Recator反應器

Reactor由三部分組成,EventLoop、Poller、Channel.
EventLoop即IO線程中的事件循環.它能確保所有註冊的事件都在EventLoop對象所在的線程中執行,不用考慮事件的併發。它是線程安全的,且允許其他線程往EventLoop裏面塞東西。

Poller 是IO multiplexing的封裝,它是EventLoop的組成,與EventLoop的生命期相當,爲EventLoop提供poll()方法。

Channel 每個Channel對象自始至終只負責一個文件描述符(fd) 的IO事件分發,但它不擁有這個fd,也不會在析構的時候關閉這個fd。每個Channel對象自始至終只屬於一個EventLoop,因此每個Channel對象都只屬於某一個IO線程。 Channel會把不同的IO事件分發爲不同的回調, 例如ReadCallback、 WriteCallback等

Reactor的實現筆記 muduo學習筆記(二)Reactor關鍵結構
Reactor部分實現源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/Reactor
Reactor的時序圖 :

EventLoop的兩個組件

TimerQueue定時器

TimerQueue 並未在類圖中單獨給出,它是EventLoop的組件,爲EventLoop提供了定時任務,和週期任務的接口。通過註冊一個Timerfd到Poller實現.

TimerQueue的實現筆記 muduo網絡庫學習筆記(三)TimerQueue定時器隊列
TimerQueue實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/TimerQueue

Eventfd

Eventfd 這個就是其他線程能往EventLoop線程裏面塞任務的實現核心,它是 一個事件文件描述符fd,EventLoop通過將它註冊到Poller,當其他線程往EventLoop裏面塞任務的時候,先將任務存儲在EventLoop的容器中,然後激活Eventfd,處理容器中存儲的任務,當然賽任務需要一把鎖來保護。

Eventfd的實現筆記 muduo網絡庫學習筆記(四) 通過eventfd實現的事件通知機制

Connector和Acceptor連接器和監聽器

Acceptor

Acceptor它是服務端TcpServer類的主要組件,封裝服務端的連接監聽部分,在非阻塞網絡編程中,accept的描述符可讀,表明有新的連接上來,連接建立後通過回調告知用戶有新的連接上來。

Acceptor的筆記 muduo網絡庫學習筆記(五) 鏈接器Connector與監聽器Acceptor
Acceptor實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/Acceptor
Acceptor的時序圖 :

Connector

在非阻塞網絡編程中,發起連接的基本方式是調用connect(2),當socket變得可寫時表明連接建立完畢,但是其中要處理各種類型的錯誤,muduo中把它封裝爲Connector class.
Connector 和 Acceptor 設計思路基本一致,只是Acceptor通過判斷套接字是否可讀來執行回調,而Connector是判斷套接字是否可寫來執行回調,但是要注意的是socket可寫不一定就是連接建立好了 , 當連接建立出錯時,套接口描述符變成既可讀又可寫,這時我們可以通過調用getsockopt來得到套接口上待處理的錯誤(SO_ERROR),如果錯誤是0表示連接成功。

Connector的筆記 muduo網絡庫學習筆記(五) 鏈接器Connector與監聽器Acceptor
Connector實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/Connector
Connector的時序圖 :

TcpConnection

Buffer muduo中的buffer通過vector和一個棧上空間實現,動態可調,其結構很精妙,感興趣的話建議直接閱讀陳碩Buffer部分設計的文章. 這個buffer主要做爲TcpConnection的組件。
Socket 封裝一個套接字,管理了這個套接字描述符的生命期,是TcpConnection的組件,TcpConnection 通過這個套接字描述符註冊讀寫事件,SocketHelp一個純接口文件,封裝了Socket的操作接口。
TcpConnection 封裝一條Tcp連接, 處理這條連接中的讀寫及錯誤,連接關閉等事件,這些事件會在TcpConnection的內部先進行處理,然後通過回調函數將TcpConnection緩衝的Buffer提供給用戶處理。

TcpServer和TcpClient

Tcpserver 主要使用組件Acceptor,有新的連接到來時會new一個TcpConnection保存在ConnectionMaps(TcpConnection共享指針的一張映射表)中,通過創建時註冊的名字索引管理所有的連接;有數據可讀時通過MessgeCallBack回調提供用戶使用。
TcpClient 主要組件Connector, 它的實現與TcpServer相似,只不過每個TcpClient只管理一個TcpConnection。

Reactor部分和EventLoop的組件理解後,TcpConnection和TcpServer部分就很好看懂了,所以也沒有單獨寫新的文章。
TcpServer實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/TcpServer
TcpClient實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/TcpClient

Tcpserver的時序圖 :

muduo中的線程安全日誌

AsyncLogging異步日誌

AsyncLogging一個C++Stream風格的多線程安全非阻塞日誌,是muduo庫中的另一個部分組成。
這個日誌使用了雙緩衝機制,這樣新建的日誌不必等待磁盤操作,也避免了每條新的日誌都觸發日誌線程,而是將多條日誌拼程一個大的buffer 和後端buffer交換,後端線程就實時將後端buffer寫入本地文件. 相當於批處理,減少線程喚醒頻率 ,大大降低開銷。
另外 ,爲了及時將 日誌消息寫入文件, 即是 buffer A 中還沒有push進來日誌 也會每週期執行一次上述的寫入操作。
但是有個問題,如果宕機,宕機瞬間緩存中的日誌肯定是還沒寫完的。
用了一陣子,總的來說,這個日誌個人很喜歡,輕巧簡潔,十分便利。
AsyncLogging日誌的格式化部分實現筆記 一個輕巧高效的多線程c++stream風格異步日誌(一)
AsyncLogging的雙緩衝機制 一個輕巧高效的多線程c++stream風格異步日誌(二)
AsyncLogging的源碼 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/AsyncLogging
直接包含Logger.hh 和 AsycnLogging.hh即可直接使用。
AsyncLogging類結構 :

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章