當我設計遊戲服務器時,我在想些什麼?(3)

有了單進程的服務器之後,在我想加入第一個遊戲功能的時候,我又糾結了,這貨不是我最後想要的東西啊,我就算用單進程寫完了整個遊戲,也絕不該是這樣的啊,我想要的是一個多進程的服務器,而且進程之間應該是可以通信的,進程還可以是動態增刪的。這樣纔可以扛住我自認爲會出現的壓力啊。


於是我開始思考,我的進程之間需要怎麼去通信,數據該怎麼去傳輸,服務器間的進程通信跟服務器與客戶端的通信方式又不相同,跟客戶端的通信,需要保證安全,同時需要數據量儘量的小,而服務器進程間卻通常不存在這樣的問題,因爲大部分的服務器進程通常都會在同一臺機器上,即使在不同的機器上,也會是在外網保護之下,所以內部通信基本不需要考慮安全問題,當然,這也是我一面之詞,我是這麼認爲的,所以我想要一種快速,高效,而且簡單的通信方式,我想要通信的雙方可以隨便進入或者離開,我想要進程的變化不影響現有的邏輯,而且我也不想要去限制進程的啓動順序,因爲那樣就必然會涉及到進程的狀態需要同步,同時我還想要pomelo那樣的無狀態進程,這樣可以方便增刪進程來改變服務器的承載能力。


衆裏尋他千百度,我發現了 zmq。一顆閃亮的積木。雖然 LGPL 協議給它抹了一點黑,但是依然掩蓋不了這種設計的光彩,它基本滿足我的所有想法。


zmq 的官方文檔寫的非常有意思,建議大家都去看看,我在 github 上找到了一個非常不錯的中文翻譯:https://github.com/anjuke/zguide-cn

基本上把這個文檔的內容全部看完,對 zmq 就瞭解完了,這也是它簡單的體現,nodejs 的 zmq 綁定存在一些問題,但都不是太大的問題,我用着用着就解決掉了。我通過 zmq 首先設計的模型是 req - rep 模型,也是最簡單,最基礎的模型,當然在 req 和 rep 之間,我使用的是 route-dealer 組件來做消息的分發,又因爲此時我已經大致想到了遊戲所需的各個功能模塊,於是我同時也抽象出不同的服務器進程,然後讓他們之間能夠相互發送消息,整個設計看起來就是這個樣子的:


上面這個看起來像菊花一樣的東西就是我最初的設計,所有的進程邏輯進程都通過 router-dealer 來進行中轉,每個進程都有若干的請求鏈接req,也有若干的響應鏈接rep,由於有多個不同的進程,所以每個進程都需要保存跟其他進程通信的 zmq鏈路,zmq 的協議,並不是一個 req 能夠對應多個 rep,而是 req 和 rep 數量需要一致,一開始我覺得這很ok,可寫着寫着就發現,這貨明顯過於複雜了,而且似乎也沒有發揮出 zmq 最大的力量,於是我又埋頭開始讀 zmq 的文檔。RTFM 也許是一個痛苦的過程,但是 zmq 的文檔寫的非常不錯,賞心悅目,我也發現了一個意外的驚喜,zmq 的異步管家模式,這種模式其實是使用 dealer 鏈路代替 req 和 rep 鏈路,這樣就可以採取一對多的方式進行消息的分發,也就不會再像我之前那樣維護一朵菊花了。npm 庫裏有另一個老外寫的 zmq 管家模式:https://www.npmjs.com/package/pigato,不過他寫的還不滿足我的需求,我就自己動手,用 js 寫了一套符合我口味的異步管家模式,後來新招了一個後臺,他在我介紹之後立馬就理解了,並且迅速的動手進行了功能的添加,證明我寫的還是不錯的:)


有了這個分發器之後,我很快就把上面的菊花改的漂亮多了,同時我還設計了自己的通信協議,裝逼的支持了很多數據類型,事實上根本沒有用,然後用 lua 寫了一個協議解析程序,可以根據協議配置文件,我用的是 ini 文件,來生成對應的 lua 版本和 js 版本的協議代碼:

接着,我還設計了一個命令行進程來管理我的這一套服務器進程系統,於是圖變成了這個樣子:


每個進程提供一條請求鏈路和一條工作鏈路,從請求鏈路發送請求信息,工作鏈路用來接收別的進程發來的請求信息並處理返回。由 broker 來對請求和工作回覆進行轉發,pub 和 sub 鏈路是爲了監聽命令行進程的消息而準備的。這樣服務器的基本框架就算完成了,我也長長出了一口氣。


菊花設計的出現說明了我並沒有完全的理解 zmq 的行爲模式,同時也表明了我對 tcp 協議的理解還欠火候。下一篇我將繼續改進這個設計。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章