語言: C++ 和 Lua 邏輯黏合劑。
如果使用正確的 LuaJIT 版本,則該程式碼已經過測試,可以在 Ubuntu x86、Ubuntu x86_64 和 Gentoo x86_64 上編譯和運行。對 libjansson 進行了修改,以刪除在整數完成後用 .0 強制格式化浮點數,以正確支援“整數”,並且仍然具有從僅支援雙精度的 Lua 的自動轉換。
鑑於 libjansson 和 google-glog 是手動建置的,它還可以在 Ubuntu 12.04 LTS x86_64 上編譯。
LuaJIT
利布夫
evhttpclient
Google-glog
google-perftools (tcmalloc) -可選,僅對原始程式碼和 makefile 進行少量修改
利布揚松
赫里迪斯
libicu - 查看本機套件管理器
curl - 查看本機套件管理器
boost -如果您用自己的東西替換侵入性指針,則可選。
可能需要修改 src/Makefile 以反映您的安裝資料夾。歡迎更強大的建置系統,但在編寫專案時並不需要。 (CMake?)
我(@zwagoth)在這裡學到了一些東西:在撰寫本文時始終發表評論。為了彌補這一點,我將記錄該程式的基本結構以幫助理解。
對於程式碼庫的混亂表示歉意。這也是我的第一個任何規模和複雜性的 C++ 專案之一。
包含協定訊息、RTB訊息以及連接、斷開等各種基本事件回呼的訊息處理的幾乎所有核心邏輯。
該檔案包含匿名函數表,這些函數以它們處理的事件命名。該文件不應儲存任何狀態,僅被視為邏輯儲存區域。該檔案的基本設計是允許膠合邏輯,而不強迫 Lua 做任何繁重的工作,並最大限度地減少 Lua 傳入和傳出的資料量,但代價是對 C++ 進行更多函數呼叫。
全域檔案命名空間中註入了四個表,它們的名稱都很短,以便於輸入:
u
:連接/字元(使用者)函數
s
:伺服器/全域狀態函數
c
: 頻道狀態函數
const
:錯誤 ID 和常數值
協定回呼函數接受兩個參數:與命令關聯的連接以及所提供的 json 參數的表副本。
連接是不透明的數字,不應以任何方式修改。更改連接的值是不安全的。你已被警告過。
聊天守護程序啟動和操作期間使用的配置變數的 lua 檔案。
簡約的閃存策略伺服器。如果您需要支援 Flash,那麼您就可以執行它。根據您的需求自訂內嵌策略。
程序入口點。處理後台執行緒和curl 的初始化。
做的事情太多了。程式碼流從Server::run()
開始。
連結流程如下:
listenCallback handshakeCallback connectionWriteCallback connectionReadCallback connectionwriteCallback
listenCallback
設定每個連接事件處理程序並將流程傳遞到...
handshakeCallback
,處理 websocket 握手的讀取。
connectionWriteCallback
在連線準備好寫入時進行處理,並在佇列中存在要寫入連線的項目時啟用。處理緩衝。
當握手階段結束時, connectionReadCallback
處理所有讀取事件。所有協定解析都發生在這裡,並且命令是從該函數內部調度和運行的。
pingCallback
處理向客戶端發送 ping 事件。如果協議發生變化,這應該是首先要做的事情之一。
connectionTimerCallback
會定期觸發並檢查連線是否已失效並清理它。
runLuaEvent
是紐特魔法。也是每個命令發生魔法的地方。這是每個命令從 C++ 程式碼轉換為 Lua 程式碼的地方。處理所有 Lua 狀態和回調,以確保 Lua 不會陷入無限循環。處理 Lua 內部的列印錯誤。
該文件儲存與頻道、連線、禁止和審核相關的所有狀態資料。
連接分為已識別和未識別的連接,因為在登入伺服器驗證它們存在之前,字元名稱是未知的,並將它們排除在可按名稱查找的字元池之外。
處理狀態保存和還原到磁碟。
作為後台執行緒運行,處理登入請求和回應並將它們傳回主執行緒。
序列化登入系統,使用全域登入佇列。這可以改進很多。
轉換為curl_multi 會很好,但涉及到與libev 的一些有趣的互動或大量的盲目輪詢。由於線程的原因,隊列在被訪問之前必須被鎖定。
基本通道類,在通道的生命週期內維護有關通道的狀態資料。所有與通道相關的低階操作都透過 Lua 中的鉤子發生在該檔案中。通常包裝在侵入式指標中以管理實例生命週期。
這就是 json 通道的序列化和反序列化發生的地方。
處理所有連接網路和調試 Lua 狀態。
維護扭結清單、狀態、狀態訊息和性別。
維護忽略和好友清單。
每個連接的句柄節流閥。
這是輸出資料緩衝發生的地方。
通常使用侵入式指標來管理實例生命週期。
維護已加入頻道的內部清單。這必須與實際的頻道使用者清單保持同步。
該文件是為少數需要原始速度而不是可自訂的功能而保留的。處理登入 ( IDN
) 命令。處理調試 ( ZZZ
) 命令。處理搜尋 ( FKS
) 指令。
Lua 檔案中屬於s
類別的所有 Lua 包裝器指令。
Lua 檔案中屬於u
類別的所有 Lua 包裝器指令。
Lua 檔案中屬於c
類別的所有 Lua 包裝器指令。
Lua 檔案中屬於const
類別的所有 Lua 包裝器值。
錯誤訊息和定義。
使用定義的巨集進行錯誤和類型檢查!如果錯誤的類型意外地作為輕量級資料傳遞到函數中,這是防止崩潰的唯一方法。
確保您的 Lua 堆疊平衡。我已經盡力確保這是真的,但很容易犯錯。
避免將表返回 Lua 程式碼,它們的建置成本很高,而且使用時間通常很短。使用最佳判斷來平衡函數呼叫的成本和建表的成本。
避免不必要地將字串傳遞給 Lua。由於記憶體副本的存在,成本可能會很高。
濫用 lua 接受多個回傳值作為本機功能。參見上面兩個註釋。
處理僅推送的 Redis 線程。是 redis 指令及其傳回值的小包裝。
使用輸入隊列來接收命令。命令以週期性方式運行,並且不保證可靠性。可以停用,停用時忽略輸入。
gdb 非常適合調試該應用程式。禁用 tcmalloc 和 LuaJIT 的 JIT 部分應該大大有助於調試崩潰(tcmalloc 可以隱藏一些輕微的堆損壞)。
tcmalloc 中的記憶體分析工具非常好。有關如何使用它的更多信息,請參閱 tcmalloc 文件。