einx:由 Golang 編寫的用於遊戲伺服器或者應用伺服器的開源框架
einx
a framework in golang for game server or app server.
a example server for einx (ofollow,noindex" target="_blank">https://github.com/Cyinx/game_server_einx )
einx 是一個由 golang 編寫的用於遊戲伺服器或者應用伺服器的開源框架。
設計核心:
- 模組與元件的組合機制,模組是邏輯核心。
- lua指令碼
- 按業務分離邏輯
- einx/db 元件化資料庫相關操作
- einx/network 元件化網路IO,目前只支援TCP
- einx/log 非同步日誌庫
- einx/timer 時間輪定時器
- einx/module 模組
- einx/component 元件
- einx/lua 指令碼相關操作
模組與元件
每個模組有且僅有一個goroutine用於處理被投遞到本模組中的訊息,在模組中的邏輯不需要考慮同步問題,簡化了邏輯開發難度,模組與模組之間可以通過RPC互動
使用 einx 搭建一個簡單的伺服器
首先安裝 einx
git clone https://github.com/Cyinx/einx.git
建立一個簡單的einx例子:
package main import ( "github.com/Cyinx/einx" "github.com/Cyinx/einx/slog" ) func main() { slog.SetLogPath("log/game_server/") slog.LogInfo("game_server", "start server...") slog.LogInfo("game_server", "hello world...") einx.Run() einx.Close() }
einx的核心是module,module中可以新增各種component作為元件:
Cyinx/einx/network網路相關的component Cyinx/einx/db資料庫相關的component
建立一個TCPServer的component管理器:
package clientmgr import ( "github.com/Cyinx/einx" "github.com/Cyinx/einx/slog" "msg_def" ) type Agent = einx.Agent type AgentID = einx.AgentID type EventType = einx.EventType type Component = einx.Component type ComponentID = einx.ComponentID type ClientMgr struct { client_map map[AgentID]Agent tcp_linkComponent } var Instance = &ClientMgr{ client_map: make(map[AgentID]Agent), } func (this *ClientMgr) GetClient(agent_id AgentID) (Agent, bool) { client, ok := this.client_map[agent_id] return client, ok } func (this *ClientMgr) OnLinkerConneted(id AgentID, agent Agent) { this.client_map[id] = agent //新連線連入伺服器 } func (this *ClientMgr) OnLinkerClosed(id AgentID, agent Agent) { delete(this.client_map, id) //連線斷開 } func (this *ClientMgr) OnComponentError(c Component, err error) { } func (this *ClientMgr) OnComponentCreate(id ComponentID, component Component) { this.tcp_link = component component.Start() slog.LogInfo("tcp", "Tcp sever start success") }
建立一個邏輯module,並將TcpServer管理器加入到module之中,伺服器就可以啟動,並監聽2345埠的請求
package main import ( "clientmgr" "github.com/Cyinx/einx" "github.com/Cyinx/einx/slog" ) var logic = einx.GetModule("logic") func main() { slog.SetLogPath("log/game_server/") logic.AddTcpServer(":2345", clientmgr.Instance) slog.LogInfo("game_server", "start server...") einx.Run() einx.Close() }
註冊訊息handler與Rpc: 註冊訊息handler需要事先註冊一個Message:
package msg_def import ( "github.com/Cyinx/einx/network" "protobuf_gen" ) type VersionCheck = pbgen.VersionCheck var VersionCheckMsgID = network.RegisterMsgProto(uint16(pbgen.MainMsgID_GENERAL_MSG), uint16(pbgen.HandlerMsgID_VERSION_CHECK), (*VersionCheck)(nil))
在註冊RPC時,使用字串作為RPC名,註冊handler時,需要使用之前註冊的MsgID
import ( "msg_def" ) var logic = einx.GetModule("logic") func InitDBHandler() { logic.RegisterRpcHandler("testRpc", testRpc) logic.RegisterHandler(msg_def.VersionCheckMsgID, CheckVersion) } func testRpc(sender interface{}, args []interface{}) { } func CheckVersion(agent Agent, args interface{}) { version_check_msg := args.(*msg_def.VersionCheck) }
註冊定時器使用module.AddTimer函式,返回值為timerID,如果要提前停止timer,可以執行module.RemoveTimer(timerid):
import ( "msg_def" ) var logic = einx.GetModule("logic") var testTimerID uint64 = 0 func InitDBHandler() { logic.RegisterRpcHandler("testRpc", testRpc) logic.RegisterHandler(msg_def.VersionCheckMsgID, CheckVersion) } func testRpc(sender interface{}, args []interface{}) { if testTimerID != 0 { logic.RemoveTimer(testTimerID) } } func TestTimer(args []interface{}) { testTimerID = 0 } func CheckVersion(agent Agent, args interface{}) { version_check_msg := args.(*msg_def.VersionCheck) testTimerID = logic.AddTimer(1000,TestTimer,1,2,"測試") }