# NettyGameServer **Repository Path**: jxzft2015/NettyGameServer ## Basic Information - **Project Name**: NettyGameServer - **Description**: No description available - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2019-10-31 - **Last Updated**: 2024-02-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README NettyGameServer == - 使用netty4.X实现的手机游戏服务器,支持tcp,udp,http,websocket链接,采用protobuf自定义协议栈进行网络通信,支持rpc远程调用,使用mybatis3支持db存储分库分表,支持异步mysql存储,db保存时同步更新reids缓存。 - 使用ExcelToCode工程,将excel数据生成java类和json数据字典,DictService直接读取json,减少数据字典部分代码。 - 使用game-executor工程,增加游戏内的异步事件全局服务,支持事件sharding,均衡的异步执行事件逻辑 - 使用netty的proxy模式,增加网关代理转发 --- **1. 代码学习研读** -- ### TCP/UDP-协议栈-格式详解 ####tcp自定义协议栈 >- 消息结构为NetMessageHead,NetProtoBufMessageBody >- 消息头NetMessageHead结构为 >- 魔法头 short head; >- 长度 int length; >- 版本号 byte version; >- 命令 short cmd; >- 序列号 int serial; >- 消息体NetProtoBufMessageBody结构为 >- 内容 bytes body; ####udp自定义协议栈 >- 消息结构为NetUdpMessageHead,NetProtoBufMessageBody >- 消息头NetUdpMessageHead结构为 >- 魔法头 short head; >- 长度 int length; >- 版本号 byte version; >- 命令 short cmd; >- 序列号 int serial; >- 玩家id long playerId; >- 玩家会话凭据 int tocken; >- 消息体NetProtoBufMessageBody结构为 >- 内容 bytes body; ### 各模块核心代码学习 ####rpc部分 > 核心代码类: >- ObjectProxy // 核心代码 >- ConnectManage //连接管理 >- RPCFuture // --- **2. 使用手册** -- ### 服务器启动 public class GameServerEx extends GameServer{ public static void main(String[] args) { GameServerEx gameServerEx = new GameServerEx(); gameServerEx.startServer(); } } ### db使用手册 > 基于spring-sharding-mybaits集成redis缓存的游戏分布式存储框架.支持将对象序列化到队列里,异步存储。 使用spring集成mybatis3垂直和水平分库mysql.使用模版编程,采用代理模式,采集变化的字段,自动完成拼写sql,降低数据库落地难度 集成Mybatis-PageHelper分页,大数据量可以分批查询。提升查询速度 #### 存储实体使用 ##### entity存储实体 >- 注解讲解 >> EntitySave 对象需要存储 >> FieldSave 字段映射到数据库 >> DbMapper 对象存储到mybatis的映射 >- 代码使用 >> @EntitySave >> @DbMapper(mapper = OrderMapper.class) ``` public class Order extends BaseLongIDEntity implements RedisInterface, AsyncSave { @FieldSave private String status; /** * @return status */ public String getStatus() { return status; } /** * @param status */ @MethodSaveProxy(proxy="status") public void setStatus(String status) { this.status = status; } @Override public String toString() { return "Order{" + "orderId=" + getId() + ", userId=" + getUserId() + ", status='" + status + '\'' + '}'; } @Override public String getUnionKey() { return String.valueOf(getUserId()+ EntityUtils.ENTITY_SPLIT_STRING + getId()); } @Override public String getRedisKeyEnumString() { return RedisKeyEnum.PLAYER.getKey(); } } ``` #####代理实体 >- 注解 >> MethodSaveProxy 类需要使用代理 会通过此注解,注入到代理对象的变化集合里 >- 生成代理 >> EntityProxyFactory entityProxyFactory = (EntityProxyFactory) classPathXmlApplicationContext.getBean("entityProxyFactory"); >> MoreOrder moreOrder = new MoreOrder(); >> MoreOrder proxyOrder = entityProxyFactory.createProxyEntity(moreOrder); >- 注解实例 >> @EntitySave >> @DbMapper(mapper = OrderMapper.class) ``` public class Order extends BaseLongIDEntity implements RedisInterface, AsyncSave { @FieldSave private String status; /** * @return status */ public String getStatus() { return status; } /** * @param status */ @MethodSaveProxy(proxy="status") public void setStatus(String status) { this.status = status; } @Override public String toString() { return "Order{" + "orderId=" + getId() + ", userId=" + getUserId() + ", status='" + status + '\'' + '}'; } @Override public String getUnionKey() { return String.valueOf(getUserId()+ EntityUtils.ENTITY_SPLIT_STRING + getId()); } @Override public String getRedisKeyEnumString() { return RedisKeyEnum.PLAYER.getKey(); } } ``` ####存储服务使用 #####同步无缓存使用 >- 注解讲解 >> DbOperation 数据存储操作insert,udapte等 >- 使用方法 >> Order order = new Order(); >> order.setUserId(TestConstants.userId); >> order.setId((long) i); >> order.setStatus("测试插入" + i); >> orderService.insertOrder(order); #####同步缓存使用 >- 使用例子 OrderService orderService = (OrderService) classPathXmlApplicationContext.getBean("orderService"); EntityServiceProxyFactory entityServiceProxyFactory = (EntityServiceProxyFactory) classPathXmlApplicationContext.getBean("entityServiceProxyFactory"); orderService = entityServiceProxyFactory.createProxyService(orderService); Order order = new Order(); order.setUserId(TestConstants.userId); order.setId((long) i); order.setStatus("测试插入" + i); orderService.insertOrder(order); #####异步缓存使用 >- 使用例子 ``` OrderService orderService = (OrderService) classPathXmlApplicationContext.getBean("orderService"); EntityAysncServiceProxyFactory entityAysncServiceProxyFactory = (EntityAysncServiceProxyFactory) classPathXmlApplicationContext.getBean("entityAysncServiceProxyFactory"); orderService = entityAysncServiceProxyFactory.createProxyService(orderService); Order order = new Order(); order.setUserId(TestConstants.userId); order.setId((long) i); order.setStatus("测试插入" + i); orderService.insertOrder(order); ``` ####压力测试 >- 填充测试环境 >> 修改TestConstants下saveSize为500000 执行com.snowcattle.game.db.service.jdbc.test.tps.singleThread.JdbcTest的main函数,插入测试数据 >- 单进程测试 >> 普通插入 >>- 修改TestConstants下saveSize com.snowcattle.game.db.service.jdbc.test.tps.singleThread.jdbcTest的main函数 >> 批量插入 >>- com.snowcattle.game.db.service.jdbc.test.tps.singleThread.JdbcBatchOriginTest的main函数 >- 多线程测试 >> 普通插入 >>- 修改TestConstants下saveSize com.snowcattle.game.db.service.jdbc.test.tps.mutilthread.sync.NoCacheTest的main函数 >> 同步缓存插入 >>- 修改TestConstants下saveSize com.snowcattle.game.db.service.jdbc.test.tps.mutilthread.sync.CacheTest的main函数 >> 异步缓存插入 >>- com.snowcattle.game.db.service.jdbc.test.tps.mutilthread.async.AsyncCacheTest的main函数 ### 数据字典 #### 数据字典生成 >- 将excel拷贝到tools/excelToCode/python/sample/excels下面 >- 修改tools/excelToCode/python/sample下的config.py文件 >>1. 修改DEFAULT_JAVA_PATH = "../../../../src/main/java/com/snowcattle/game/service/dict/entity" 参数 >>2. 修改DEFAULT_DICT_PATH = "../../../../src/main/resources/dict" 参数 > 修改DEFAULT_JAVA_PACKAGE = "com.snowcattle.game.service.dict.entity" 参数 >- 执行export.sh文件`` #### 数据字典使用 >- 唯一键 Bullet bullet = dictService.getIDict(dictModleType, id, Bullet.class); >- 重复键 List roleSkills = dictService.getIDictArray(dictModleType, id, RoleSkill.class); --- **3. 实例工程** -- >- netty-game-server-start --- **4. 测试用例调试** -- >- 启动服务器 >>运行game-core模块下com.snowcattle.game.bootstrap 类GameServer >- 测试用例 >>运行game-core模块下test下面net.client包 --- **5 打包&部署&启动** -- >- path:game-core模块deploy >> ├── build.sh // 打包脚本 >> ├── config >> ├── └── launch.sh // 启动备份脚本 >> └── dist.sh //解压build.sh 包并调用启动脚本 ---