# game_server_engine **Repository Path**: poetryBoy/game_server_engine ## Basic Information - **Project Name**: game_server_engine - **Description**: 基于现有理解撸一套Java游戏服务端的引擎框架 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 3 - **Created**: 2022-04-13 - **Last Updated**: 2025-09-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: 游戏开发, Java, 服务端, Server, 框架 ## README ## game_server_engine ### 介绍 基于现有理解撸一套Java游戏服务端的引擎框架 可以更简单高效的搭建游戏服务器,做到高性能同时高效率的开发逻辑 ### 应用上下文AppContext >应用入口和上下文,用来把引擎组件按模块关联启动,做到统一启动、管理和关闭,内置spring的ioc容器 ### Hutool工具包 >一些工具类是必不可少的,这里使用了Hutool的工具套件 >使用它的setting用作启动配置加载和管理,还有一些其他封装,很好用 ### 通用策略池 >一般不同策略或者接口实现管理的单例,如果要通过类型或者一些操作获取到指定策略使用,都离不开扫描包和映射缓存,这里实现一套策略池, > 通过启动扫描带指定注解的接口,然后根据注解配置扫描接口的实现类,通过指定的Selector保存起来,可以省去写类似功能时需要考虑的扫描和缓存需求。 >>框架内提供了3种Selector: >> 1. 自定义键值对`StrategyCustomMapSelector`,一般通过一个Key映射一个策略,可以设置默认策略。 >> 2. 随机`StrategyRandomSelector`,获取时随机一个 >> 3. 单个`StrategySingleSelector`,单个实现,和单例的区别在于它可以随时覆盖替换来更换实现的逻辑 ### 时间轮算法实现TimerWheel >内部实现了一套多层级时间轮`Wtimer`,使用单线程的scheduledExecutorService调度,每层时间轮有N个槽,每个槽管理一个双向链表存放任务, >单线程的调度,全程无锁执行,执行的任务放到专门的任务线程池上执行。该时间轮几乎满足全部的定时需求,并且设计上有很强的扩展性, >规则上都留有接口用于自定义时间轮(`IRoundWheel`和`WRule`) >* 框架内默认提供的时间轮规则是根据现实时间的,类似钟表,有四个层级:**秒轮-分轮-时轮-周轮**, >同钟表一样,秒轮转动60次为一圈,同时上一层时间轮转动(分轮),以此类推,之所以没有月轮年轮,是因为月和年的天数不固定, >不如不要,超出周轮的时间会记周期`round`+ 1,转动到指定槽上(**比如第5秒就是第5个槽**)会进行降级处理,(**例如转到5分,槽中有任务, >会把任务下放到秒轮,如果10秒执行,放到第10个槽,以此类推**)直到没有下一级时间轮,就会放入线程池中执行 >* 默认时间轮的最低时间精度是秒,并且定时任务的误差会在20ms上下,如果有更高精度的需求,可以使用自定义的规则创建时间轮 ### Actor线程模型 >在传统线程池的模型上,游戏的逻辑需要处理太多并发问题,玩家自己的请求都可能不在同一线程上,这里引入actor模型并且绑定线程Id,通过指定的id, >锁定到相同的线程上,例如玩家A的请求绑定到线程1,那这次在线他都一直在1线程上串行操作。每个线程上有一个任务阻塞队列,所有任务是丢到队列中串行执行, >减少逻辑上的多线程难度 ### 事件总线EventBus >游戏中少不了的就是事件驱动了,通过观察者模式,把事件同步或者异步通知给所有监听事件的方法,是一种优秀的解耦设计,模块之间不用强关联,扩展性和可用性非常高 ### 比反射性能更好:通过字节码操作对象 >反射性能差是众所周知的,但很多时候又必须用到反射去调用,就比如上面的事件总线,持有的观察者对象需要通知时,原来的做法是缓存`method`,然后反射调用`invoke`, >但是在游戏逻辑中,事件的通知时非常频繁的,这样的反射调用会带来很大的性能消耗和延迟。 >* 采用ASM字节码操作,更高性能,类似相关的三方包有`ReflectASM`,`cglib`,`javaassist`等,这里采用cglib,后面很多模块比如数据层ORM,RPC都使用这个来提高反射性能 ### 数据层ORM(Mysql) >游戏中少不了一套ORM的数据层,这里手动实现了一套基于原生Jdbc的对象映射数据层(原生调用少部分使用了hutool的封装) >* 支持多数据源配置,默认使用`HikariCp`连接池,支持自动创表创库,并且每次启动都会检查和同步表结构,将数据层做到最简便,可配置开启 >* 实体和仓库设计,实体属性通过注解配置,类似Jpa,但比Jpa更强大的是有一套自动类型转换器,支持所有基本类型和对象类型的属性 (自定义类入库当前是使用json序列化`fastJson`) >* 实体仓库有三层实现,最底层的直接操作库的`EntityRepository`,上一层`DelayRepository`新增了延迟入库,再上一层是`CacheRepository`, >使用Caffeine缓存来缓存数据,减小数据库压力。 >* 当前缓存层不适用集群和分布式,后续考虑添加redisson缓存层适用集群和分布式服务器 >* 实体内使用集合或者map时必须使用线程安全的类型,否则会抛错 ### 网络层Netty >采用netty做游戏的长连接,框架内封装了`tcp`,`udp`,`websocket`的服务端和客户端,简便使用。协议包体`Packet`一个4位的int请求号,然后是具体业务的数据长度和内容, >框架内实现了protobuf协议的包体封装和协议业务处理器接口,可以直接应用到和客户端交互上。 >`最上层Packet不能修改,如果要修改数据包体或者新增加密、CRC等,可以自定义继承重写Codec` ### RPC >对于服务器来讲,怎么少的了Rpc远程调用,虽然市面上有不少优秀的rpc框架,但如果仅用于Java游戏端来讲,要不就太复杂不好用,要不就需求不满足,同时秉着学习的态度,也正好自己手写一份RPC框架 >* 基于上面的网络层实现,底层使用tcp的服务和客户端 >* 协议上采用接口,一个服务就是一个接口,客户端获取到接口实现的代理对象,然后调用服务器的具体实现返回结果 >* 序列化采用`ProtoStuff`,该序列化是在protobuf基础上修改后,不需要冗长的proto文件,同时达到高性能的一种序列化方式 >* 服务注册中心默认提供了zookeeper实现,也可以实现`Registry`提供其他类型注册中心 >* 服务发现提供了精确的条件查找和自带的负载均衡器(目前有随机、首个、索引哈希),更多负载选取需求可以自己实现 >* 该rpc使用简单,性能高,同时满足各类需求,可代理服务类同步调用,也可以获取Invoke异步调用 >* 更多细节可以查看源码 ### JVM关闭的Shutdown hook管理器 >因为各个模块肯定是互有关联,但如果任由java自己提供的shutdownHook注册,执行顺序是不确定的,很可能关闭时会报错,所以框架内实现了一个Hook管理器, >可以设置优先级或者依赖,最终关闭时会排序按顺序关闭,彻底没有了关闭的先后顺序问题 ### Jedis >封装了新版Jedis,支持根据配置切换模式,三种模式:单机、分片、集群 >* redisson很强大,但是感觉太重了,所用依赖非常多,如果有需求可以加入redisson ### 策划配置表解析和管理 >框架内实现了一套配置表解析套件,类似ORM,将配置表解析成Model对象,放入缓存,并且可以自定义缓存仓库用以实现特殊存放需求, >当前内部实现了2种解析器:**Excel**、**Csv**,会根据文件后缀自适应使用对应解析器,更多类型的解析器等待后续添加或者自行加入 ### 更多问题讨论可以添加QQ534262841 #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request