Skynet是什麼呢?
我們希望遊戲伺服器能夠充分利用多核優勢,将不同的業務放在獨立的執行環境中處理,協同工作。這個執行環境最早期望是利用作業系統的程序,後來發現如果必定采用嵌入式語言如Lua,那麼獨立的作業系統程序的意義不大。LuaState已經提供了良好的沙盒,隔離不同執行環境,而多線程模式可以使得狀态共享、資料交換更加高效。但是多線程模型又存在諸多弊病,比如複雜的線程鎖、線程排程問題等。這些問題都可以通過減小底層的規模精簡設計,最終把危害限制在很小的範圍内。
早期的Skynet v0版本中,Skynet是使用Erlang+C driver開發的,Erlang中的多程序似乎是:每個玩家都有一個代理服務。當在在40v40時,使用桌上型電腦測試時是存在困難的,于是Cloud滋生了重新考慮之前的設計的念頭。由于之前的設計方案是:每個代理的狀态都需要通知給其他代理,這造成了大量内部消息重複,這明顯是可以優化的。
掂量下,如果設計一個共享無鎖隊列,避免了無謂複制,内部消息包可以降低90%,整體性能能提升15%左右。為了驗證Skynet v0的性能,編寫了一個簡單的Skynet v1,v1完全使用C作底層通信架構,摒棄了之前ZeroMQ提供通信的部分,提供Lua接口編寫應用服務,整體代理在4000多行的C代碼和1000多行的Lua代碼,完成了Skynet v0一樣的功能。
Skynet提供了一個簡潔、穩定、高效、高可用的分布式服務開發架構。
Skynet是一個輕量級通用的伺服器基礎架構
Skynet是基于C與Lua的開源服務端并發架構,使用單程序多線程Actor模型。
Skynet伺服器支援10K+用戶端接入和處理
Skynet目前規模是8K多行的C代碼和2K多行Lua代碼,實作了一個多線程高并發的線上遊戲背景服務架構,提供定時器、開發排程、服務擴充架構、異步消息隊列、命名服務等基礎能力,支援Lua腳本。
Skynet是一個輕量級網絡伺服器架構而非完整的遊戲服務端,它是服務端的最底層架構,和遊戲業務相關的服務都是基于此架構之上開發的。其功能隻是管理好服務(加載和排程)和服務之間的調用(請求和響應)。
Skynet特點的什麼
少量C代碼和大量Lua代碼組成
基于Actor模型,天然多線程。
天然內建網絡、資料庫通路功能
使用Lua的協程處理消息永不堵塞
自帶叢集功能
官方隻支援Linux
Skynet的優點是什麼
高低級語言配合
Skynet是融合了低級語言C消息架構和進階動态語言Lua的混合體,這種結構稱為hybird framework混合架構,使用運作高效的C來編寫服務節點,使用Lua開發高效且安全隔離的上層業務。
元件化能力
Skynet核心(C部分)自身支援加載子產品*.so,可使用C語言編寫性能有要求的服務節點,通過消息與其他節點配合。而Lua又是對C語言極為友好的動态語言,是以可找到很多Lua的C擴充。
Skynet核心是什麼
C實作的消息循環群組件加載機制
Lua實作的以消息為中心的進入退出協程的包裝層
Skynet核心解決什麼問題呢?
作為核心功能,Skynet僅解決一個問題:把符合規範的C子產品從動态庫(.so檔案)中啟動起來,綁定一個永不重複(即使子產品退出)的數字ID作為其處理器handle。這裡我們稱子產品為服務service,服務之間可以自由發送消息,每個子產品可以向Skynet架構注冊一個回調函數callback,用來接收發給它的消息。每個服務都是一個個消息包驅動,當沒有包到來時,它們就會處于挂起狀态,對CPU資源零消耗。如果需要自主邏輯則可利用Skynet提供的timeout消息定期觸發。
Skynet提供了名字服務,可以給特定的服務起一個易讀的名字,而不是使用ID來指代。因為ID和運作時狀态相關,無法保證每次啟動服務都有一緻的ID,當名字卻可以。
Skynet核心不解決什麼問題?
Skynet的消息傳遞都是單向的,以資料包為機關傳遞。并沒有定義類似于TCP連接配接的概念,也沒有約定RPC調用的協定。不規定資料包的編碼方式,也沒有提供一緻的複雜資料結構的列集API。
Skynet原則上主張所有伺服器都在同一個作業系統程序上協作完成,是以在核心層内部考慮跨機器通訊機制。Skynet不為單獨服務的崩潰、重新開機提供支援,和普通的單線程程式一樣,你要為你代碼中的bug和意外負責。和作業系統不同的是:作業系統會認為使用者程序都是不可靠的,它不會讓一個使用者程序的錯誤影響到另一個程序。但Skynet内所有的服務都有統一的目的,為遊戲的最終客戶服務。是以某個環節出了錯誤都可能是緻命的,是以沒有必要被問題隔離開。
簡單來說,Skynet隻負責把一個資料包從一個服務内發送出去,讓同一程序内的另一個服務接收然後調用對應的callback函數處理。Skynet保證子產品的初始化過程時,每個獨立的callback都是互相線程安全的。編寫服務的人不需要特别的為多線程環境考慮任何問題,專心處理發送給它的一個個資料包即可,其實這就是Erlang的Actor模型。
為了提供高效的服務間通訊,Skynet并不關心資料包是怎樣被打包的,它甚至不要求這個資料包内的資料是連續的,雖然這樣做很危險,在跨機通訊中除非你保證你所有的資料包絕對不被傳遞到目前所在程序中。因為它僅僅是把資料包的指針以及聲明的資料包長度傳遞出去。由于服務都是在同一個程序内,是以接收方取得這個指針後就可以直接處理其引用的資料了。這個機制在必要時,可以保證絕對的零拷貝,幾乎等于在同一線程内做一次函數調用的開銷,當然這隻是Skynet提供性能上可能性。推薦一種更為可靠但性能略低的解決方案:約定每個服務發送出來的包都複制到用malloc配置設定出來的連續記憶體。接收方在處理完這個資料塊,也就是在處理的callback函數調用完畢時,會預設調用free函數釋放掉所占用的記憶體空間。簡單來說就是:發送方申請記憶體而接收方釋放記憶體。
更多面試資料與往期視訊請關注公衆号