1. 程式人生 > >伺服器熱更新的討論

伺服器熱更新的討論

我們之前的伺服器是多程序純C#架構的伺服器。最近遊戲上線,遇到有時候需要線上修bug的問題。之前對程式碼熱更新預料不足,導致在線上出了一些bug時非常被動,往往需要重啟伺服器解決問題,影響前期的體驗。我們的遊戲型別是一個RPG卡牌遊戲,操作偏向於單機向,大量依賴於廣播的操作比較少。

這周的時間裡,也考慮了幾種方案,下面對比下思考的結果。

嵌入動態指令碼

首先,C#+python或者lua的遊戲伺服器解決方案沒有成功例項。現在最有名的ulua和xlua都是基於unity引擎的,在伺服器的效能和承載上沒有無法驗證。沒有成熟框架情況下,指令碼和非指令碼分層等如何來設定,還是需要比較多的設計和開發時間的。

其次,程式設計師使用指令碼語言進行程式設計比用C#進行敏捷程式設計效率會差,vs等工具對於效率、正確性提升的紅利會消失。此外,也是公司自己的原因,整體轉變帶來了不可預估的時間成本,以後的專案使用lua開發正確性能不能保障,會不會出現更多的bug?

其他的一些問題。比如一些自動化的程式碼(配置表,網路資料傳送)往往需要在宿主語言和指令碼語言上都實現一份等。這些工作會帶來不少額外的工作量。

mono程式碼熱更新

因為最終伺服器是執行在linux環境下,用mono執行的,而mono是完全開源的。曾經有一位163的牛逼同事,實現了這麼一套熱更新的方案。方案原理是可以在執行時讓mono把記憶體中的xxx.exe的某一些程式碼片段改掉,或是把入口重定向掉。

這種方法可以用來修一些bug,但很難去做一些小型新功能的支援。因為原型僅僅可以支援程式碼片段IL程式碼的改寫,沒法支援對新型別的操作等。

另一個缺點是,部分語法比較難以替換,比如泛型函式,閉包訪問hold住的物件等,這些在虛擬機器層面實現也是比較複雜的語法,在做mono熱更新時就會是一些坑點的。如果我們可以完美支援mono熱更新,需要了解很多底層語法的機制,那開發量可能與重寫一個mono處於一個量級。

最後一個缺點是,熱更新操作不如lua熱更新直觀。需要先編譯出新的xxx.exe,然後通過工具生成出新老exe的il差,並匯出成mono可以識別的文字。最後,線上觸發mono去載入這個文字進行替換。而這裡的步驟在lua熱更新中,只需要簡單的把錯誤的xxx.lua替換一次就可以了,我們的操作明顯複雜。

實現無狀態伺服器

這一點主要是因為我們期望的遊戲伺服器型別是一個比較單機化的類卡牌遊戲,才有可能這麼去實現一個伺服器架構。無狀態伺服器的最大優點是,邏輯和儲存分離。也就是邏輯處理伺服器只有處理器,沒有狀態,有點類似於actor模型;而儲存伺服器(可以是redis,也可以自己實現)則之提供儲存和讀取資料等簡單穩定介面,保障儲存伺服器的穩定,就沒有對儲存伺服器進行熱更新的需求了。

這種框架之下,邏輯處理伺服器就可以比較隨意的重啟了,以此實現了基於冷更新的伺服器更新功能,但是玩家並不會感受到遊戲更新或是斷開。這種情況下,同時執行的幾個伺服器程序在某段時間內可能是不同版本的,可以實現一個版本逐步更替的過程。

這個方案會帶來一些複雜性

  • 需要一個排程伺服器(CONTROLSV)排程新的LOGICSV替代舊的伺服器的過程,所以這個伺服器需要維護已有的伺服器組。
  • 需要拆分出一個能夠配合儲存流程的轉有儲存節點STORESv
  • LOGICSv的邏輯程式碼,當需要訪問和儲存資料時,原本只需要訪問記憶體物件,現在需要非同步訪問。增加了程式碼編寫的複雜度。
  • LOGICSv節點因為沒有資料,如果要發起一些主動的update操作,也要非同步去db取資料再改寫。更嚴重的是,在操作大量資料時(比如對所有線上玩家update),會造成內部io和訪問延遲比較大,並可能造成和玩家執行緒存取資料的衝突。