轉(zhuǎn)自云風(fēng)BLOG http://blog.codingnow.com/2006/10/multi_process_design.html
目前,我們的游戲服務(wù)器組是按多進(jìn)程的方式設(shè)計的。強(qiáng)調(diào)多進(jìn)程,是想提另外一點(diǎn),我們每個進(jìn)程上是單線程的。所以,我們在設(shè)計中,系統(tǒng)的復(fù)雜點(diǎn)在于進(jìn)程間如何交換數(shù)據(jù);而不需要考慮線程間的數(shù)據(jù)鎖問題。
如果肆意的做進(jìn)程間通訊,在進(jìn)程數(shù)量不斷增加后,會使系統(tǒng)混亂不可控。經(jīng)過分析后,我決定做如下的限制:
-
如果一個進(jìn)程需要和多個服務(wù)器做雙向通訊,那么這個進(jìn)程不能處理復(fù)雜的邏輯,而只是過濾和轉(zhuǎn)發(fā)數(shù)據(jù)用。即,這樣的一個進(jìn)程 S ,只會把進(jìn)程 A 發(fā)過來的數(shù)據(jù)轉(zhuǎn)發(fā)到 B ;或把進(jìn)程 B 發(fā)過來的數(shù)據(jù)轉(zhuǎn)發(fā)到 A 。或者從一端發(fā)過來的數(shù)據(jù),經(jīng)過簡單的協(xié)議分析后,可以分發(fā)到不同的地方。例如,把客戶端發(fā)過來的數(shù)據(jù)包中的聊天信息分離處理,交到聊天進(jìn)程處理。
-
有邏輯處理的進(jìn)程上的數(shù)據(jù)流一定是單向的,它可以從多個數(shù)據(jù)源讀取數(shù)據(jù),但是處理后一定反饋到另外的地方,而不需要和數(shù)據(jù)源做邏輯上的交互。
-
每個進(jìn)程盡可能的保持單個輸入點(diǎn),或是單個輸出點(diǎn)。
-
所有費(fèi)時的操作均發(fā)到獨(dú)立的進(jìn)程,以隊列方式處理。
-
按功能和場景劃分進(jìn)程,單一服務(wù)和單一場景中不再分離出多個進(jìn)程做負(fù)載均衡。
性能問題上,我是這樣考慮的:
我們應(yīng)該充分利用多核的優(yōu)勢,這會是日后的發(fā)展方向。讓每個進(jìn)程要么處理大流量小計算量的工作;要么處理小流量大計算量的工作。這樣多個進(jìn)程放在一臺物理機(jī)器上可以更加充分的利用機(jī)器的資源。
單線程多進(jìn)程的設(shè)計,個人認(rèn)為更能發(fā)揮多核的優(yōu)勢。這是因?yàn)闆]有了鎖,每個線程都可以以最大吞吐量工作。增加的負(fù)擔(dān)只是進(jìn)程間的數(shù)據(jù)復(fù)制,在網(wǎng)游這種復(fù)雜邏輯的系統(tǒng)中,一般不會比邏輯計算更早成為瓶頸。如果擔(dān)心,單線程沒有利用多核計算的優(yōu)勢,不妨考慮以下的例子:
計算 a/b+c/d+e/f ,如果我們在一個進(jìn)程中開三條線程利用三個核同時計算 a/b c/d e/f 固然不錯,但它增加了程序設(shè)計的復(fù)雜度。而換個思路,做成三個進(jìn)程,第一個只算 a/b 把結(jié)果交給第二個進(jìn)程去算 c/d 于之的和,再交個第三個進(jìn)程算 e/f 。對于單次運(yùn)算來算,雖然成本增加了。它需要做額外的進(jìn)程間通訊復(fù)制中間結(jié)果。但,如果我們有大量連續(xù)的這樣的計算要做,整體的吞吐量卻增加了。因?yàn)樵谒? 某次的 a/b 的時候,前一次的 c/d 可能在另一個核中并行計算著。
具體的設(shè)計中,我們只需要把處理數(shù)據(jù)包的任務(wù)切細(xì),適當(dāng)增加處理流水線的長度,就可以提高整個系統(tǒng)的吞吐量了。由于邏輯操作是單線程的,所以另需要注意的一點(diǎn)是,所有費(fèi)時的操作都應(yīng)該轉(zhuǎn)發(fā)到獨(dú)立的進(jìn)程中異步完成。比如下面會提到的數(shù)據(jù)存取服務(wù)。
對于具體的場景管理是這樣做的:
玩 家連接進(jìn)來后,所有數(shù)據(jù)包會經(jīng)過一個叫做位置服務(wù)的進(jìn)程中。這個進(jìn)程可以區(qū)分玩家所在的位置,然后把玩家數(shù)據(jù)分發(fā)到對應(yīng)的場景服務(wù)進(jìn)程中。這個位置服務(wù)同 時還管理玩家間消息的廣播。即,單個的場景(邏輯)服務(wù)并不關(guān)心每個數(shù)據(jù)包為哪幾個玩家所見,而由這個服務(wù)將其復(fù)制分發(fā)。
當(dāng)玩家切換場景,場景服務(wù)器將玩家的數(shù)據(jù)發(fā)送給數(shù)據(jù)服務(wù),數(shù)據(jù)服務(wù)進(jìn)程 cache 玩家數(shù)據(jù),并將數(shù)據(jù)寫入數(shù)據(jù)庫。然后把玩家的新的場景編號發(fā)回位置服務(wù)進(jìn)程,這樣位置服務(wù)器可以將后續(xù)的玩家數(shù)據(jù)包正確的轉(zhuǎn)發(fā)到新的場景服務(wù)進(jìn)程中。
掉落物品和資源生產(chǎn)同樣可以統(tǒng)一管理,所以的場景(邏輯)進(jìn)程都將生產(chǎn)新物件的請求發(fā)給物品分配服務(wù),由物品分配服務(wù)生產(chǎn)出新物件后通知位置服務(wù)器產(chǎn)生新物品。
這樣一系列的做法,最終保證了,每個場景服務(wù)器都有一個唯一的數(shù)據(jù)源——位置服務(wù)進(jìn)程。它跟持久化在數(shù)據(jù)庫中的數(shù)據(jù)無關(guān),跟時鐘也無關(guān)。由此帶來的調(diào)試便利是很顯著的。
最近,面臨諸多進(jìn)程的設(shè)計時,最先面臨的一個復(fù)雜點(diǎn)在于啟動階段。顯然,每個進(jìn)程都配有一套配置文件指出其它進(jìn)程的地址并不是一個好主意。而為每個 服務(wù)都分配一個子域名在開發(fā)期也不太合適。結(jié)果我們采取了一個簡單的方案:單獨(dú)開發(fā)了一個名字服務(wù)器。它的功能類似 DNS ,但是可以讓每個進(jìn)程自由的注冊自己的位置,還可以定期匯報自己的當(dāng)前狀態(tài)。這樣,我們可以方便的用程序查詢到需要的服務(wù)。名字服務(wù)器的協(xié)議用的類似 POP3 的文本協(xié)議,這讓我們可以人手工 telnet 上去查閱。我相信以后我們的維護(hù)人員會喜歡這樣的設(shè)計的。:D
以上,國慶假期結(jié)束以來的工作。感謝項目組其他同事的辛勤編碼。