目錄html
本篇結束muduo網絡庫部分學習的筆記,總結一下muduo網絡庫的模塊組成,同時會提供筆記中個模塊的實現代碼,這些模塊代碼單獨抽出同時去除了muduo中對boost的依賴,改用c++11中的組件或者用單獨的類替換,會使得muduo的各個組件會更爲簡潔易學。
基於C++11的muduo :https://github.com/BethlyRoseDaisley/SimpleMuduo 代碼可能還有些bug和不完善的地方,簡單使用和學習的話徹底夠用,也可自行完善。linux
muduo是一個高質量的事件驅動型的網絡庫,其核心代碼不超過4500行,使用的non-blocking IO(IO multiplexing)+ one loop per
thread模型。此模型每一個IO線程裏面只有一個事件循環(即一個Reactor),處理讀寫和定時事件,激活的事件經過回調方式提供用戶處理業務邏輯。
在linux下的話,能夠把事件當作一個文件描述符,換句話也就是說一個file descriptor只能由一個線程讀寫。
一個線程最多隻有一個EventLoop,而EventLoop中的循環便是在不停的監視這些描述符,當描述符可讀或可寫的時候,經過回調函數提供給用戶處理。
這樣咱們能夠很方便地把不一樣的socket套接字的描述符放到不一樣的線程去, 也能夠把一些socket放到一個線程裏,這樣這些socket就是線程安全的,由於始終只有EventLoo所在線程在讀寫它們,極大的下降了咱們的編程複雜性。c++
使用起來的話,它對外看上去應該這個樣子,EventLoop一直處在事件循環中,經過IO複用機制select/poll/epoll回調激活的事件。
git
muduo的組件大體可劃分爲 5個部分, Reactor、TimerQueue和Eventfd、Acceptor和Connector、TcpConnection、TcpServer和TcpClient。
muduo網絡部分的簡化類圖
github
若是隻注重服務端的話,能夠TcpClient省去,Poller在muduo中是個純虛基類,如今用poll(2)具體化它,省略它們後的結構應該是這樣的。
EventLoop和Poller及Channel組成Reactor部分、Acceptor做爲TcpServer的監聽器、TcpConnection負責處理socket的讀寫等事件、而TcpServer處理TcpConnection讀寫完成後的回調事件。
編程
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的時序圖 :
網絡
TimerQueue
並未在類圖中單獨給出,它是EventLoop的組件,爲EventLoop提供了定時任務,和週期任務的接口。經過註冊一個Timerfd到Poller實現.
TimerQueue的實現筆記 muduo網絡庫學習筆記(三)TimerQueue定時器隊列
TimerQueue實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/TimerQueue
Eventfd
這個就是其餘線程能往EventLoop線程裏面塞任務的實現核心,它是 一個事件文件描述符fd,EventLoop經過將它註冊到Poller,當其餘線程往EventLoop裏面塞任務的時候,先將任務存儲在EventLoop的容器中,而後激活Eventfd,處理容器中存儲的任務,固然賽任務須要一把鎖來保護。
Eventfd的實現筆記 muduo網絡庫學習筆記(四) 經過eventfd實現的事件通知機制
Acceptor
它是服務端TcpServer類的主要組件,封裝服務端的鏈接監聽部分,在非阻塞網絡編程中,accept的描述符可讀,代表有新的鏈接上來,鏈接創建後經過回調告知用戶有新的鏈接上來。
Acceptor的筆記 muduo網絡庫學習筆記(五) 連接器Connector與監聽器Acceptor
Acceptor實現部分源碼及簡單測試 : https://github.com/BethlyRoseDaisley/SimpleMuduo/tree/master/Acceptor
Acceptor的時序圖 :
在非阻塞網絡編程中,發起鏈接的基本方式是調用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的時序圖 :
Buffer
muduo中的buffer經過vector和一個棧上空間實現,動態可調,其結構很精妙,感興趣的話建議直接閱讀陳碩Buffer部分設計的文章. 這個buffer主要作爲TcpConnection的組件。
Socket
封裝一個套接字,管理了這個套接字描述符的生命期,是TcpConnection的組件,TcpConnection 經過這個套接字描述符註冊讀寫事件,SocketHelp一個純接口文件,封裝了Socket的操做接口。
TcpConnection
封裝一條Tcp鏈接, 處理這條鏈接中的讀寫及錯誤,鏈接關閉等事件,這些事件會在TcpConnection的內部先進行處理,而後經過回調函數將TcpConnection緩衝的Buffer提供給用戶處理。
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的時序圖 :
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類結構 :