文章目錄
-
- 1.聊天伺服器
- 2.消息格式
- 3.時序圖
- 4.通過程式設計技巧降低鎖競争和提高并發效率
1.聊天伺服器
-
聊天伺服器(MuduoManual.pdf P66)
(1)examples/asio/chat/server.cc 單線程
(2)examples/asio/chat/server_threaded.cc,多線程TcpServer,并用mutex來保護共享資料mutex,由于mutex的存在,多線程并不能并發執行
(3)examples/asio/chat/server_threaded_efficient.cc,借shared_ptr實作copy-on-write的手法來降低鎖競争
(4)examples/asio/chat/server_threaded_highperformance.cc,采用thread local變量實作多線程高效轉發
-
聊天伺服器示意圖
服務端收到用戶端的hello消息,服務端轉發hello消息到C1,C2,C3用戶端
(P44-P45)muduo庫使用例子(三):聊天伺服器
2.消息格式
- 消息分為標頭與包體,每條消息有一個4位元組的頭部,以網絡序存放字元串的長度。包體是一個字元串,字元串也不一定以’\0’結尾。比方說有兩條消息"hello"和"chenshuo",那麼打包後的位元組流是:
-
0x00,0x00,0x00,0x05, ‘h’,‘e’,‘l’,‘l’,‘o’,0x00,0x00,0x00,0x08,‘c’,‘h’, ‘e’,‘n’,‘s’,‘h’,‘u’,‘o’
共21位元組。
對于傳輸層來講這是一個位元組流,是沒有邊界的。在應用層将其解析出來,解析成2條消息,先解析標頭,再解析包體,這才是一條完整的消息
3.時序圖
-
coder decoder組合以下就是codec編解碼,前者取前2個字元,後者取前3個字元;
LengthHeaderCodec就是對聊天伺服器的消息進行編解碼;
(1)當可讀事件到來,回調LengthHeaderCodec中的onMessage,然後對收到的消息進行解碼decode,先解析標頭,再解析包尾,然後再把包體的字元串傳給ChatServer,回調onStringMessage;
()ChatServer收到消息,将其轉發給線上的用戶端,調用LengthHeaderCodec::send(),因為要進行編碼(標頭+包體),send(Buffer)發送給線上的用戶端
(P44-P45)muduo庫使用例子(三):聊天伺服器 -
eg:44\jmuduo\examples\asio\chat\server.cc----------單線程版本
44\jmuduo\examples\asio\chat\codec.h
44\jmuduo\examples\asio\chat\client.cc
44\jmuduo\examples\asio\chat\server_threaded.cc----------多線程版本
-
eg測試:單線程:44\jmuduo\examples\asio\chat\server.cc
44\jmuduo\examples\asio\chat\client.cc
- 測試:服務端 用戶端1
(P44-P45)muduo庫使用例子(三):聊天伺服器 (P44-P45)muduo庫使用例子(三):聊天伺服器 用戶端2(P44-P45)muduo庫使用例子(三):聊天伺服器 (P44-P45)muduo庫使用例子(三):聊天伺服器 (P44-P45)muduo庫使用例子(三):聊天伺服器
4.通過程式設計技巧降低鎖競争和提高并發效率
-
問題:45\jmuduo\examples\asio\chat\server_threaded.cc,多線程TcpServer,并用mutex來保護共享資料mutex,由于mutex的存在,多線程并不能并發執行,而是串行的。因而存在較高的鎖競争,效率比較低。
(1)eg:C1向伺服器端發送一條消息hello,伺服器通過一個IO線程轉發給所有的用戶端,與此同時C2向伺服器端發送一條消息hello2,伺服器端通過另一個IO線程轉發給所有用戶端,由于鎖的存在,這兩個線程并不能并發執行,而是串行的。這個時候,用戶端數目比較大,第二條消息hello2到達各個用戶端的延遲也比較大。
(2)大并發的一個殺手就是鎖競争
-
examples/asio/chat/server_threaded_efficient.cc,借shared_ptr實作copy-on-write的手法來降低鎖競争
(1)shared_ptr是引用計數智能指針,如果目前隻有一個觀察者,那麼引用計數為1,可以用shared_ptr::unique()來判斷(為真引用計數為1,否則不為1;shared_ptr共享資源既有讀端,也有寫端)
(2)對于write端,如果發現引用計數為1(隻有一個寫者來通路它),這時可以安全地修改對象,不必擔心有人在讀它。
(3)對于read端,在讀之前把引用計數加1,讀完之後減1,這樣可以保證在讀的期間其引用計數大于1,可以阻止并發寫。
(4)比較難的是,對于write端,如果發現引用計數大于1,該如何處理?既然要更新資料,肯定要加鎖,如果這時候其他線程正在讀,那麼不能在原來的資料上修改,得建立一個副本,在副本上修改,修改完了再替換。如果沒有使用者在讀,那麼可以直接修改。
-
eg:45\jmuduo\examples\asio\chat\server_threaded_efficient.cc
45\jmuduo\examples\asio\chat\server_threaded_highperformance.cc