忙活了一個多星期,差不多把基於TCP的高並發串連網路架構測試穩定了。
目的:利用多線程把網路連接及資料包壓縮/解壓、加密/解密等等耗時的操作分流(asio對這些沒有原生的支援),順帶提供線程池架構。只對遊戲邏輯層暴露出單線程的外觀,隔離底層多線程的複雜度。
結構如(未遵循什麼標準,將就著看吧):
TCPSessionHandler:暴露給邏輯層的類,內部負責通過TCPIOThreadManager跟掛載於某個線程的TCPSession進行互動,對上層屏蔽多線程細節。聲明如下:class TCPSessionHandler : public std::enable_shared_from_this<TCPSessionHandler>,<br /> public boost::noncopyable {<br /> public:<br /> // ==================== TYPEDEFS =======================================<br /> // ==================== LIFECYCLE =======================================<br /> TCPSessionHandler();<br /> virtual ~TCPSessionHandler() {}<br /> // ==================== OPERATIONS =======================================<br /> // sends message to remote endpoint, the content of message would be consumed<br /> void SendMessage(NetMessage& message);<br /> // sends message to remote endpoint, the content of message would be consumed<br /> void SendMessage(NetMessage&& message);<br /> // closes the session<br /> void Close();<br /> // true if the session is closed.<br /> bool IsClosed() { return kInvalidTCPSessionID == session_id_; }<br /> // called when connection complete.<br /> virtual void OnConnect() = 0;<br /> // called when NetMessage received.<br /> virtual void OnMessage(const NetMessage& message) = 0;<br /> // called when TCPSession closed.<br /> virtual void OnClose() = 0;<br />};
(註:本文代碼風格盡量遵循 google c++ style guide
)
NetMessageList:NetMessage定義為邏輯上有明確分界的網路訊息,一個或多個NetMessage組成NetMessageList。
TCPIOThreadManager:管理一個或多個TCPIOThread,其中一個TCPIOThtread作為主線程邏輯運行。
CommandList:線程間互動的命令隊列,也是整個架構中唯一的線程間同步方式,稍候詳述。
TCPIOThread:IO線程,通過CommandList也可作為背景工作執行緒使用,每個線程使用asio的io_service處理多個TCPSession。
NetMessageFilterInterface:網路訊息過濾器介面(省略了Interface因為太長了),可以自定製,通常封裝組包、壓縮、加密等流程,以適應不同的邏輯層協議需求。
TCPSession:後台網路連接,不區分服務端/用戶端,負責處理網路資料的發送和接收。
外層還有兩個類:TCPServer和TCPClient,可以關聯到同一個TCPIOThreadManager,以適應多服架構中某台伺服器既是TCP伺服器又是其它伺服器的用戶端的需求。
範例程式碼:
int main(int argc, char** argv) {<br /> TCPIOThreadManager manager(1, // thread num<br /> boost::posix_time::millisec(2)); // sync interval<br /> unsigned short int port = 20000;<br /> TCPServer server({boost::asio::ip::tcp::v4(), port},<br /> manager,<br /> &MyHandler::Create,<br /> &MyFilter::Create);<br /> manager.Run();<br /> return 0;<br />}
用戶端也類似。
線程同步策略:
上述線程間同步採用了Command模式,藉助了c++ 0x的function。CommandList的定義:
typedef std::list<std::function<void ()>> CommandList;
每個線程每隔一段時間就把要發往其它線程的Command批量發送,因為list的splice只是幾個指標的操作,這個過程可以通過自旋鎖高效的完成(psydo code):
CommandList thread1.commands_to_be_sent_;<br />CommandList thread2.commands_received_;<br />//thread1:<br />{<br /> thread1.commands_to_be_sent_.push_back(command);<br /> ...;<br /> thread2.spinlock_.lock();<br /> thread2.commands_received_.splice(thread2.commands_received_.end(),<br /> thread1.commands_to_be_sent_); //把thread1.commands_to_be_sent_串連到thread2.commands_received_的尾部<br /> thread2.spinlock_.unlock();<br />}<br />//thread2:<br />{<br /> CommandList templist;<br /> thread2.spinlock_.lock();<br /> templist.swap(thread2.commands_received_); //把commands_received_跟暫存佇列交換再處理,<br /> //減少lock的時間<br /> thread2.spinlock_.unlock();<br /> for (auto it = templist.begin(); it != templist.end(); ++it)<br /> (*it)();<br /> templist.clear();<br />}<br />
從TCPSessionHandler到TCPSession的NetMessageList的發送,就是通過這套機制來實現,幾乎可以忽略線程鎖開銷。
剩下的問題:NetMessage內部變長緩衝用了vector來實現,動態記憶體分配可能會帶來效率上的隱患。實際應用如果確證,可以用記憶體池,但是多線程的高效記憶體池稍微複雜。相關思路改天再寫。