ballbet官網
  咨詢電話:18850431399

貝博ballbet

muduo網絡庫架構總結

目錄

muduo網絡庫簡介muduo網絡庫模塊組成Recator反應器EventLoop的兩個組件TimerQueue定時器EventfdConnector和Acceptor連接器和監聽器AcceptorConnectorTcpConnectionTcpServer和TcpClientmuduo中的線程安全日志AsyncLogging異步日志

本篇結束muduo網絡庫部分學習的筆記,總結一下muduo網絡庫的模塊組成,同時會提供筆記中個模塊的實現代碼,這些模塊代碼單獨抽出同時去除了muduo中對boost的依賴,改用c++11中的組件或者用單獨的類替換,會使得muduo的各個組件會更為簡潔易學,不過代碼還不完善,簡單使用和學習的話完全夠用。

muduo網絡庫簡介

muduo是一個高質量的事件驅動型的網絡庫,其核心代碼不超過4500行,使用的non-blocking IO(IO multiplexing)+ one loop perthread模型。此模型每個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/ReactorReactor的時序圖 :

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與監聽器AcceptorAcceptor實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/AcceptorAcceptor的時序圖 :

Connector

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

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

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/TcpServerTcpClient實現部分源碼及簡單測試 : 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類結構 :

在高邮干什么最赚钱