A developing game server framework,based on Entity Component System(ECS).
A developing game server framework,based on Entity Component System(ECS).
重构 RockGO ECS engine: https://github.com/zllangct/ecs
基于ECS(Entity component System)构建的分布式游戏服务端框架,同时提供Actor模型,目标是致力于快速搭建轻量、高性能、高可用的 分布式游戏后端,以及其他分布式后端应用。
组件化的构架使得开发者不需要任何改动就能轻松实现一站式开发,分布式部署,规避分布式调试的困难,提升开发效率。框架提供udp、 tcp、websocket常用网络协议,同时提供优雅的协议接口,可让开发者轻松实现其他如kcp等网络协议的定制。
$ go get -u github.com/zllangct/RockGO当然用最简单的hello world告诉你一切都是如此的简单, 完整代码参见example: SingleNode ,更多的用法,参见example目录。
Config:
…Server:
…Client:
//默认网关采用websocket协议,可以使用任意websocket客户点连接测试。example中提供一个laya客户端供测试使用。ECS全称Entity-Component-System(实体-组件-系统),是基于组合优于继承,即将不变的部分使用继承以方便复用, 将多变的部分用组合来方便拓展,是按照这种原则的一种设计模式。当然这种设计模式带来了许多比OOP更容易容实现 的特性,当然ECS并不是抛弃OOP,这是一个度的问题。ECS的特点和优势,暴雪在分享《守望先锋》的ECS服务构架的文 章中已经阐述比较详细(中文译文戳这里),也可以看看云风博客中的 讨论 (继续戳),此处都不赘述了。在服务端程序中 的优势,这里简要概括几点:
Component尽量按照原子性原则设计,只要有合适的功能拆分粒度,服务端可以随性所欲的组合,这在分布式构架
中至关重要,关系到服务端节点间功能的拆分组合。比如中心服务节点担任了游戏大厅和注册登录两项服务,随着用户
规模的扩大,中心节点需要把注册登录拆分出来单独成为节点。在ECS构架中,这将是非常容易的一件事,很少或者
甚至不需要任何修改一行功能代码就能实现,因为节点本身就是功能组件的组合,分服不过是根据配置文件重新组合一
次。这也微服务的思想走在了一起,微服务往往在服务间的调用走的是网络调用,但ECS构架下可以轻松的实现网络本地
调用的自主切换。单服就是真正的单服,本地调用,减少不必要的网络消耗,而不是多个服务部署在了同一台物理机上。 ECS构架下,实体和组件都能运行时添加删除,框架提供了Initialize、Awake、Start、Update、Destroy 内置系统,
这些内置系统能保证每一个功能组件,或者组件组合所需的完整生命周期。
功能的拓展,大多情况是新组件,新系统的设计,耦合性极低。
所以对象都有完整的生命周期,Destroy系统可以保证每个对象在销毁时,得到正确的处理,实现更优雅的停机处理。
所有组件,实现持久化接口之后,都具备序列化的能力。配合优雅的停机,很容易实现停机后的恢复。
每个系统都不会遍历所有的对象,只会过滤出感兴趣的组件,专人专事,效率集中,减少调用,例如不需要Update处理的组件,便
不用实现Update接口,Update系统将不会遍历此组件。
分布式调试的麻烦,这里并不存在,单机开发,随心大胆的断点,最后仅仅是一个配置参数完成分布式部署。
有空再详细写,反正知道对方的ActorID,无论他在哪儿,无论活在那个节点,Tell() 都能告诉他。框架内自主实现本地调用和RPC 调用的分流。Actor模式的特性与优势,看官自行G或者B。千万别问为什么有了ECS还能有Actor,并不冲突,ECS也是基于OOP实现的,所以Actor基于 ECS实现,而且比OOP实现Actor更简单。
RPC的性能与可靠性是分布式系统中最重要的一环,游戏要的是效率,服务治理方面够用就行,所以并未选择市面上流行的服务治理型 的RPC调用框架,并且游戏构架本身就具备了服务治理的能力,但RPC框架自带的治理能力又不足以支撑游戏需求,所以略显多余,大体量的框架 复杂的环境配置大大的增加了维护、定制、学习、部署、迁移的难度,尤其对中小型开发者不友好。既要满足中小型项目需要的简易性, 又具有大型项目的扩展性,框架选用了go自带的rpc框架,其性能有目共睹,测评参照这里(戳我就行), 基于net/rpc作了轻量化定制,添加了必要的功能特性:
框架支持的网络协议有:TCP、UDP、Websocket、http,封包格式如下:
| 协议名称 | 协议格式 | 长度 | 数据类型 |
|---|---|---|---|
| TCP | Length-[Type-Data] | 4 - [ 4 - n ] | 二进制 |
| UDP | Length-[Session-Type-Data] | 4 - [ 4 - 4 - n ] | 二进制 |
| Websocket | Type-Data | 4 - n | 二进制 |
http建议直接在网关组件中使用gin、fasthttp等http处理框架对应路由处理函数,http使用途中极有可能与页面有关,虽然 集成到上述方式路由中十分简单,但不建议这样处理,这样僵化了http的灵活性和丰富的功能特性。
框架内可以很轻松的实现自定义协议的扩展,对原有协议都是非侵入的依赖,只需要简单封装,比如各种可靠UDP协议,KCP、UDT、 ENET等,只需要实现以下述接口:
//网络协议
type ServerHandler interface {
Listen() error //监听
Handle() error //处理
}
//链接对象
type Conn interface {
WriteMessage(messageType uint32, data []byte) error //消息发送
Addr() string //目标地址
Close() error //关闭
}
//解包协议
type Protocol interface {
ParsePackage( []byte) (int, int) //包处理
ParseMessage( context.Context, []byte)([]uint32,[]byte) //消息处理
}
网络协议的实现可参照TCP、UDP和Websocket。各种网络协议所提供链接对象可不相同, 需要实现连接对象接口进行统一,供Session使用。网络协议和链接对象接口的实现相对简单,不容 易产生歧义,其中解包协议参照TCP和Websocket用法:
…自定义序列化协议需要实现以下接口:
//消息解析协议
type MessageProtocol interface {
Marshal( interface{})([]byte,error) //序列化
Unmarshal( []byte, interface{})error //反序列化
}框架提供消息的路由,无需用户手动对应消息的处理方法,框架根据函数自动判断是否为消息处理函数。需要满足以下条件 :
参见:
…当然用户可以不用使用框架自带的消息路由方法,可以实现NetAPI接口自定义消息路由规则:
type NetAPI interface {
Init() //初始化
Route(*Session, uint32, []byte) //反序列化并路由到api处理函数
Reply(session *Session,message interface{})error //序列化消息并发送至客户端
} 致谢: gin — gin-gonic、websocket—gorilla
、go-component—shadowmint、TarsGo—TarsCloud
喜欢的朋友给个星星~
【RockGO交流群①】(已满,请加②群)
No open issues yet, or sync has not completed.