一个.NET 开发的分区分服的游戏服务器框架GeekServer

大家好,我是宝弟!

今天给大家推荐一个开源的分区分服的游戏服务器框架GeekServer,GeekServer采用C# .Netcore开发,开发效率高,性能强,跨平台,并内置不停服热更新机制。可以满足绝大部分游戏类型的需求,特别是和Unity3D协同开发更佳。.

功能

1.跨平台

使用C# .Netcore开发(可以跨平台,可以跨平台,可以跨平台),.Netcore现在功能和性能都已经十分强大和稳健,不管是在windows还是linux上部署起来都很简便。

2.全面异步编程

全部采用异步编程(async/await),让逻辑代码变得整洁优雅,清晰易懂,让代码写起来行如流水。

3.TPL(Task Parallel Library) Actor模型

GeekServer的Actor模型构建于强大的TPL DataFlow之上,让Actor模型如虎添翼。(不了解Actor模型,可以搜一下相关资料,Akka,Orleans都是采用的Actor模型)了解更多

4.Actor入队透明化

GeekServer内部会自动处理线程上下文, 编译期间会通过Source Generator自动生成入队代码, 开发人员无需关心多线程以及入队逻辑, 只需要像调用普通函数一样书写逻辑。了解更多

5.Actor死锁检测

Actor模型本身是存在死锁的情况,且不容易被发现。GeekServer内部可检测环路死锁(即:A->B->C->A),并采用调用链重入机制消除环路死锁。了解更多

6.支持不停服更新

采用组件+状态的设计,状态只有属性,没有方法,组件只用方法,没有属性,并通过代理的方式全部放到热更dll中,运行时重新加载dll即可热更所有逻辑。了解更多

7.网络模块

网络模块替换了原来的DotNetty,采用Asp.Net的默认服务器Kestrel,支持协议多(Tcp,udp,Http123,websocket,signalr等),而且性能比dotnetty高很多了解更多

8.持久化透明

采用Nosql作为数据存储,状态的持久化全透明,框架会自动序列化/反序列,让开发人员更加专注于业务逻辑,无需操心数据库。了解更多

9.Timer/Scheduler/Event

内置线程安全的Timer,Scheduler,Event系统,轻松应对游戏服务器常见的定时,任务计划,事件触发等业务需求。了解更多

10.定期释放不活跃内存数据

以功能系统级别的粒度,定期剔除内存中不活跃的玩家数据,尽最大可能减少服务器内存开销。

11.高效的通信协议(基于MessagePack)

Geek.MsgPackTool (注意:需要安装.Net5.0,MessagePack的MPC工具对5.0有依赖)[MessagePack]对多态支持不够友好,GeekServer提供了工具来生成多态注册信息,序列化和反序列化效率极高,同时序列化之后的数据极小,数据传输效率很高。了解更多

12.一键导表工具(GeekConfig)

GeekConfig是一个一键导表工具,将策划配置表,转化为二进制数据,并提供了方便快捷的API供游戏调用

13.数据库客户端(GeekDB.GUI)

GeekDB.GUI是一个数据库客户端,GeekServer支持内嵌(RocksDB)和直连MongoDB的模式,但是存放的数据都是通过MessagePack序列化之后的二进制,此工具用于对这些二进制数据进行浏览。

运行
  1. 安装.NetCore6.0或更新的SDK,协议工具需要安装.Net5.0(MessagePack代码生成工具依赖了此版本)

  2. 安装mongodb4.x

  3. 打开git clone本项目https://github.com/leeveel/GeekServer.git

  4. 运行Tools/ExcelGen/ExcelToCode.exe 点击[服务器-ALL]导出配置表

  5. 用VisualStudio2022打开GeekServer.sln 启动GeekServer.App

  6. 启动GeekServer.Test (一个1000人登录的demo)

  7. 打开UnityDemo工程,打开SampleScene,运行查看日志(检查Main Camera上是否有脚本丢失,如果有请挂载GameMain.cs)

最佳实践

GeekServer有严格的书写规范检查,如不符合规范编译直接报错

1.CompAgent不能被二次继承,Agent继承的需求理论上很少,如果有请采用组合模式

2.为CompAgent中需要被外部提供服务的接口,添加【Api】注解

3.CompAgent中非【Threadsafe】的【Api】接口只能是异步函数

4.CompAgent中不要书写构造函数,重写Active函数来完成初始化工作

5.大部分情况下你都应该使用await等待来书写逻辑,不需要等待的方法请加上【Discard】注解,如:通知全服玩家,就没必要等待一个通知完成后再通知下一个。同时Source Generator在编译期间对标记了【Discard】的函数做了处理,内部直接返回了Task.CompletedTask,所以外部使用下划线丢弃或是用await都是等价的,为了规范统一,可以全部使用await。这样有个好处,就是可以在编译期间检查所有Agent中的代码,如有发现使用了弃元运算符(_ = DoSomething())则提示代码编写不符合规范。

public Task NotifyAllClient(){   for(int i=0; i<clients.count; i++)   {     //_ = NotifyOneClient(clients[i].roleId);   //对于标记了[Discard]的函数,等价于上面一行代码   await NotifyOneClient(clients[i].roleId);   }}
[Api][Discard]public virtual Task NotifyOneClient(long roleId){   //...   //...}

6.CompAgent中为需要提供给外部访问接口,标记[Api]注解,如果不加外部又有访问,则会有线程安全问题,除非此接口本身就是线程安全的(标记了[ThreadSafe]注解)。

public class ServerCompAgent : StateCompAgent<ServerComp, ServerState>{    private Task TestScheduleTimer(){        LOGGER.Debug("ServerCompAgent.TestSchedueTimer.延时1秒执行.每隔3秒执行");        return Task.CompletedTask;    }
    /// <summary>    /// 由于此接口会提供给其他Actor访问,所以需要标记为[Api]    /// </summary>    /// <returns></returns>    [Api]    public virtual Task<int> GetWorldLevel()    {        return Task.FromResult(State.WorldLevel);    }
}
补充说明

server_config.json配置

server_config.json中的配置在Settings 中解析,每个字段的作用参考Settings注释

唯一Id生成

GeekServer提供的IdGenerator 可用于全局id生成,比如roleId,公会id等;给定服务器id,保证全局唯一(30年内)。GeekServer为serverId预留了16位,即0-65535有效,我们推荐serverId从10000起。

日志

GeekServer使用NLog采集日志,采集方式包含控制台和文件。由于控制台占用IO,会占用大量资源,只在debug模式下输出。特殊的,LOOGER.DEBUG()只会在debug模式下打印在控制台,不会采集到日志文件。

温馨提示

服务器区别于客户端,服务器在非重大问题的情况下,是不能直接退出的,更不能出现闪退。GeekServer做了异常处理,在监听到没处理的异常时会自动走关服流程(actor回收,回存数据。。。)。理论上开发者应该确保所有的逻辑异常都能被try/catch到,然后视情况判断是丢弃异常还是关服。

 资源获取方式 

https://github.com/leeveel/GeekServer