這周的工做主要是寫代碼。 html
開發計劃制定好後,咱們便分頭寫代碼去了。咱們但願一期早點作出能夠運行的東西來,一切都從簡。總體的代碼量並很少,若是硬拆成不少份讓不少人來作的話,估計設計拆分方案,安排工做,協調每一個人寫的東西這些比一我的所有實現一遍的工做量還要大的多。 java
因此,最終就是兩我的在作。怪物公司在弄客戶端的東西,蝸牛同窗包乾了服務器。好吧,基本沒個人事了,我就是那個打醬油的,好聽點說,就是設計方案。固然,事情沒多少,空下來的時間也能夠幹活。訓練本身能夠找到事情作,並真的作有用的事情,仍是很難的。 程序員
話說回來,咱們在這麼一個簡單的框架下,一開始肯定了要採用一些現成的技術方案,即要用到 Redis , Google Protobuffer , ZeroMQ 。 正則表達式
Redis 和 ZeroMQ 是我最先選的,想了好久。 數據庫
採用 Redis 是由於歷史上,我參與的項目都沒有大規模使用 SQL 數據庫的傳統。這和 MMO 這種特定應用有關。在 Web 開發中,面對的用戶是臨時的,不依賴固定鏈接的。你不肯定用戶在不在那裏,你不肯定同時要面對的用戶有多少。你須要從小到大,採用一種可擴展容量的方案。這個時候,成熟的 SQL 數據庫方案是首選。 服務器
從軟件開發角度看,數據庫是 MVC 模式中的 M 。以 MVC 模式解決問題,M 如何實現,採用 SQL 方案只是一種可能,毫不是惟一選擇。換個角度考慮,若是是一個桌面軟件,爲何大部分的 M 卻沒有采用數據庫,而更多的是在內存中直接構建數據結構呢?性能恐怕只是一個緣由,更重要的緣由是面對的用戶的行爲不一樣。 網絡
爲何 MMORPG 服務器,至少在網易歷史上的多款遊戲,沒有使用 SQL 服務作 M 。一部分緣由是,網易遊戲的開發源頭是 Mud ,Mudos 並無使用 SQL 做爲 M ,另外一部分緣由是,MMORPG 面對的,同時須要服務的用戶有限。而用戶須要操做的數據大部分限制在用戶相關的數據體內。以外的數據體很是少。及時數據總量很大,但一層索引簡單(以用戶 id 爲索引)。每一個用戶都是爲它持續服務,數據易變。這種行爲下,從 MVC 角度看,更接近網絡應用以前的軟件設計。 數據結構
固然你甚至能夠把 M 只在邏輯上劃分出來,物理上並不切換,也就是沒有獨立意義上的數據庫服務器。這樣絕對性能最高,其實只是實現了一個簡單的單機遊戲,容許經過網絡輸入多條操做流,並把行爲反饋經過網絡發送回去罷了。甚至比單機遊戲更簡單,由於沒有圖形控制部分。 框架
若是程序不出問題,機房不停電,能夠一直的跑下去,不用考慮數據儲存問題。數據持久化不過是爲了容災罷了。把一些結構化數據持久化到硬盤上,最簡潔的方案就是寫操做系統層面的文件,必定比再使用一個數據庫要輕量,乾淨的多。 工具
有了以上背景,就不難理解,爲何我對 Redis 天生有好感。咱們並無改變設計思路,它是一直延續下來的。Redis 幫助了咱們將數據服務拆分出來。固然,MMORPG 也在發展,以上提到的用戶應用環境也在逐步變化,咱們在軟件設計上也會跟進這些變化。這些就是後話了。
ZeroMQ 呢,我是但願有一個穩健,簡潔的多進程通信方案的基礎。ZeroMQ 是不錯的一個。至少比 OS 的 Socket 庫要實用的多。它提供了更好的模式 。這是我最爲看中的。另外,這是一個 C 接口的庫,容易 binding 到不一樣的語言下使用。
在這個問題上,蝸牛同窗是反對使用 ZeroMQ 的。對他的全部反對意見,我都持保留態度。但我尊重開發人員的意願。畢竟許多代碼不會是我本身來寫。蝸牛同窗但願採用 Erlang + C Driver 的方式來驅動整個框架。也就是用 Erlang 來作通信上的數據交換。其它可能採用的開發語言,都經過 Driver 的方式插入到 Erlang 的框架中去。
我我的以爲這樣作的確可行,加上蝸牛同窗有好幾年 Erlang 開發經驗,他能擔負起實現框架的責任。我不是特別喜歡這個方案是由於,Erlang 這個東西仍是太龐大了。我對龐大的東西天生反感。雖然以蝸牛同窗的原話說,Erlang 以及他的 OTP 能寫出這麼多行代碼來有他的必要性。咱們用 C/C++(Python/Lua/Golang 等等) + ZeroMQ 實現一個拙劣的方案出來,只會漏洞百出。
我我的是不覺得然的,畢竟已經作了 10 年的網絡遊戲,對這個領域已經很熟悉了。對於陌生領域,咱們會面對許多未知的問題;但在熟悉領域,不管怎麼作,方案都不可能太拙劣。只要保持最基本的簡潔,我是有信心保證能夠解決 MMORPG 中的各類需求的。作出來的東西也能很穩定。關鍵點在於,它會足夠簡單,能輕鬆的理解實現的每一行代碼。
不過爭論都放在一邊。個人原則是,最終採用實現者自選的方案,只要它沒有明顯的問題。
咱們最終不採用 ZeroMQ 。
Google Protobuffer 我不是很喜歡的。但採用它是多個角度妥協的結果。其實我更願意自定義一套協議。而只是裁剪 GPB 的功能。
我認爲,GPB 在最底層的協議編碼定義上,作的仍是不錯的。改進它是多餘的。做爲一種協議定義,協議描述語言也算定義的不錯。但只到此爲止。接下來的部分就不甚滿意。
GPB 協議自己,默認也是用 GPB 自己定義編碼出來的。這看起來很 Cool ,但我不甚喜歡。固然全部完備的相似協議都應該有描述本身的能力。對於描述本身這件事情來講,GPB 仍是稍顯複雜了。
好比,若是你不借助已經有的 GPB 代碼和工具,很難解析一個 GPB 協議。就比如,若是世界上第一款 C 編譯器,就是最難實現的 C 編譯器。由於大部分的 C 編譯器是用 C 寫的,實現一個 C 編譯器,就陷入了先有雞仍是先有蛋的問題。
在這類問題上,聽說 Lisp 比 C 要乾的漂亮。不過我仍是和世界上大多數程序員同樣,用 C 多一些。好吧,咱們仍是繼續用 GPB 好了。
google 在實現和使用 GPB 的時候,默認採用了一種爲每一個協議,生成一組代碼的方式。而不是提供一套 C/C++ 庫,供其它語言作 binding 。這也是我所不喜的。或許是爲了性能考慮,但總以爲彆扭。若是把 GPB 換成正則表達式來看,你就能理解個人心情。
現存的大多數正則表達式的實現,都是提供一組 API ,容許使用者把須要的模式以一種人類可讀的串形式,編譯爲另外一種計算機方便處理的數據結構。當你須要的時候,使用這個數據結構,交給庫,就能夠匹配,替換字符串了。若是默認的選擇是把正則表達式編譯成 C 代碼,而後你用的時候再 link 到你的項目中,恐怕用的人要瘋掉了。固然,生成代碼這種能夠帶來更高的運行性能。
唔,其實這只是怎樣使用 GPB 這種協議的問題,和協議定義關係不大。惋惜 google 在開源之初就給出了官方的方案,引導其它語言也如法泡製,成了 GPB 的慣用法。老實說,對於 C++ ,這麼作性能是不錯的(其實也未必)。換到 Python 裏,就很是低下了。去年我按個人思路實現了 lua 的 protobuf 解析庫 ,性能能夠達到和 C++ 版本差距不到一個數量級,甚至快過 java 版。
這周的剩餘時間我都在寫一個純粹的 C 版的 protobuf 庫,不依靠代碼生成器的。但願可以做爲它語言使用 GPB 的基礎。別的語言只須要作 binding 就夠了。這玩意挺難寫,光接口設計我就改了兩版。今天終於快收工了。過兩天再寫一篇文章專門談談這個問題吧。固然,還有開源。
既然在 GPB 上花了這麼多功夫,固然,採用 GPB 就是最後的決定了。