最簡(jiǎn)單的游戲服務(wù)器只有一個(gè)進(jìn)程,就是單點(diǎn)。如果這個(gè)過程退出,整個(gè)游戲世界都會(huì)消失。游戲服務(wù)器托管。在這個(gè)過程中,由于需要處理并發(fā)的客戶端數(shù)據(jù)包,有很多選擇方法:
每次接收到用戶會(huì)話時(shí),都會(huì)建立一個(gè)線程。這個(gè)用戶會(huì)話往往是用客戶端的TCP連接來表示的,這樣每次調(diào)用一個(gè)包從套接字中讀寫,都可以使用阻塞模式,編碼直觀簡(jiǎn)單。游戲客戶端的線程數(shù)和連接數(shù)一樣多。但是這種方案也有明顯的缺點(diǎn),就是服務(wù)器容易產(chǎn)生大量的線程,很難控制內(nèi)存占用,線程切換也會(huì)造成CPU的性能損失。更重要的是,多線程下讀寫同一塊數(shù)據(jù)需要處理鎖問題,這可能會(huì)使代碼變得非常復(fù)雜,造成各種死鎖bug,影響服務(wù)器的穩(wěn)定性。
為了節(jié)省線程的創(chuàng)建和釋放,建立了線程池。當(dāng)每個(gè)用戶會(huì)話建立后,應(yīng)用到線程池以供處理線程使用。當(dāng)用戶會(huì)話結(jié)束時(shí),線程不會(huì)退出,而是將該線程的使用“釋放”給線程池。線程池可以很好的控制線程數(shù)量,防止用戶激增對(duì)服務(wù)器造成連接沖擊,形成排隊(duì)機(jī)制。但是線程池本身的實(shí)現(xiàn)比較復(fù)雜,需要嚴(yán)格遵守“應(yīng)用”和“釋放”線程的調(diào)用規(guī)則,否則會(huì)出現(xiàn)線程泄漏,耗盡線程池。
在游戲行業(yè),為了獲得高性能,使用Linux的epoll作為網(wǎng)絡(luò)API是一種常見的選擇。游戲服務(wù)器進(jìn)程中最常見的阻塞調(diào)用是網(wǎng)絡(luò)IO,所以使用epoll后,整個(gè)服務(wù)器進(jìn)程可能會(huì)變得完全沒有阻塞調(diào)用,所以只需要一個(gè)線程。這樣徹底解決了多線程的鎖問題,簡(jiǎn)化了并發(fā)編程的難度。但是“所有通話不得阻塞”的約束條件并不是那么容易遵守的。比如一些數(shù)據(jù)庫(kù)API被阻塞;另外,單個(gè)進(jìn)程、單個(gè)線程只能使用一個(gè)CPU,無(wú)法充分利用目前多核多CPU服務(wù)器中的CPU資源。異步編程是基于“回調(diào)”的,這就導(dǎo)致很多回調(diào)函數(shù)被定義,一個(gè)進(jìn)程中的邏輯是用幾個(gè)不同的回調(diào)函數(shù)來寫的,這對(duì)代碼的讀取是非常不利的。對(duì)于這個(gè)編碼問題,coroutine可以更好的幫助,所以現(xiàn)在流行異步和Coroutine的結(jié)合。無(wú)論如何,異步單線程模型仍然是許多團(tuán)隊(duì)的首選,因?yàn)樗阅芎茫恍枰l(fā)思維。
這是一個(gè)基于異步單線程模型的演化模型。這個(gè)模型一般有三種類型的線程:主線程、IO線程和邏輯線程。這些線程在內(nèi)部都以完全異步的方式運(yùn)行,它們通過一個(gè)無(wú)鎖的消息隊(duì)列相互通信。有不懂的請(qǐng)咨詢夢(mèng)飛服務(wù)器了解。