本篇結束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類結構 :