# WLEngine-Asyncio **Repository Path**: weilin3101/wlengine-asyncio ## Basic Information - **Project Name**: WLEngine-Asyncio - **Description**: 参考tornado的基本模型。通过asyncio + selector实现的Python异步IO网络库。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-02 - **Last Updated**: 2026-03-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # WLEngine-Python [TOC] ## 概述 一个基于 Python asyncio 封装的轻量级分布式游戏服务端框架,采用多进程架构,通过自定义的 TCP 网络层与 RPC 机制实现服务间通信。项目包含完整的服务端框架(`framwork`)、模板服务端(`temple_server`)和模板客户端(`temple_client`),开箱即用。 ### 核心特性 - **asyncio + selector 线程** 混合事件循环,兼顾 IO 多路复用与异步编程 - **msgpack** 二进制序列化协议,消息紧凑高效 - **链式 RPC** 调用语法(`RpcObj.RRpcGetXxx().MethodName(args)`),通过 Python `__getattr__` 动态代理实现 - **多进程架构**:login / gam / gate / gas / ats / gcc 等服务各自独立进程 - **一键启停**:通过 `setup.py` 自动生成 bat 脚本,`server.bat` 一键拉起全部服务 - **定时器系统**:支持单次(Once)和循环(Loop)Tick,基于 asyncio Future --- ## 项目结构 ``` wlengine-asyncio/ ├── Readme.md # 项目说明 ├── __init__.py ├── empty.py # 实验/工具脚本 │ ├── config/ # 配置与部署 │ ├── client_config.json # 客户端配置(login 服务器地址) │ ├── server_config.json # 服务端配置(各进程 IP/端口) │ ├── setup.py # 根据配置生成启动脚本 │ ├── setup.bat # 执行 setup.py 的快捷入口 │ └── bat/ # 自动生成的启动脚本目录 │ ├── server.bat # 一键启动所有服务 │ ├── login.bat # 启动 login 服务 │ ├── gam.bat # 启动 gam 服务 │ ├── gate1.bat # 启动 gate1 服务 │ ├── gas1.bat # 启动 gas1 服务 │ ├── ats1.bat # 启动 ats1 服务 │ └── gcc.bat # 启动 gcc 服务 │ ├── framwork/ # 核心框架层 │ ├── app.py # App 基类(生命周期 Init/Loop/Unit) │ ├── gac_app_base.py # 客户端 App 基类 │ ├── gas_app_base.py # 服务端 App 基类 │ ├── ioloop.py # asyncio 事件循环 + selector 线程 │ ├── iostream.py # TCP 连接 IOStream(读/写/事件分发) │ ├── iomsg.py # 消息协议(msgpack 序列化/反序列化) │ ├── net_util.py # Socket 工具(Bind / Close) │ ├── rpc_wrapper.py # 链式 RPC 调用封装 │ ├── configurator.py # 配置读取器(客户端/服务端) │ ├── const.py # 常量定义(路径、RPC 协议枚举) │ ├── common_util.py # 通用工具(守护进程、JSON 读取) │ ├── decorator.py # 装饰器(singleton / override) │ ├── id_mgr.py # ID 生成器(UUID / 自增 / 范围) │ ├── logger.py # 日志系统(彩色终端输出 + 文件写入) │ └── tick.py # 定时器管理(循环 Tick / 单次 Tick) │ ├── temple_client/ # 模板客户端 │ ├── main.py # 客户端入口(模拟 Tick 驱动) │ └── gac_app.py # 客户端 App 实现 │ └── temple_server/ # 模板服务端 ├── login/ # 登录服务 │ ├── main.py # 入口 │ └── login_app.py # LoginApp 实现 ├── gas/ # GameServer 服务 │ ├── main.py # 入口 │ └── gas_app.py # GasApp 实现 ├── gam/ # GameManager 服务(待实现) ├── gate/ # 网关服务(待实现) ├── ats/ # ATS 服务(待实现) └── gcc/ # GCC 服务(待实现) ``` --- ## 架构设计 ### 整体架构 ``` ┌───────────┐ TCP ┌──────────────────────────────────────┐ │ Client │ ◄──────────────────► │ Server Cluster │ │ (GacApp) │ msgpack + RPC │ │ └───────────┘ │ ┌───────┐ ┌──────┐ ┌───────────┐ │ │ │ Login │ │ Gate │ │ GAS (x N) │ │ │ └───────┘ └──────┘ └───────────┘ │ │ ┌───────┐ ┌──────┐ ┌───────────┐ │ │ │ GAM │ │ ATS │ │ GCC │ │ │ └───────┘ └──────┘ └───────────┘ │ └──────────────────────────────────────┘ ``` ### 服务进程说明 | 进程 | 说明 | 默认端口 | |------|------|---------| | **login** | 登录服务,处理客户端认证 | 4000 | | **gam** | GameManager,全局管理服务 | 4001 | | **gate** | 网关服务,客户端连接代理 | 5000 | | **gas** | GameServer,游戏逻辑服务(可多开) | 5100 | | **ats** | ATS 服务 | 5200 | | **gcc** | GCC 服务 | 5300 | ### 网络模型 框架采用 **asyncio 事件循环 + 独立 selector 线程** 的混合模型: 1. **主线程**:运行 `asyncio.get_event_loop().run_forever()`,处理所有业务逻辑回调 2. **Selector 线程**:在独立线程中运行 `selectors.DefaultSelector.select()`,监听所有 socket 事件 3. **线程协作**:Selector 线程通过 `call_soon_threadsafe` 将就绪事件投递回主线程的 asyncio 循环 ``` ┌─────────────────────────┐ ┌──────────────────────────┐ │ Main Thread │ │ Selector Thread │ │ (asyncio event loop) │ │ (selectors.select()) │ │ │ │ │ │ CallSoon() ◄────────────┼─────┤ call_soon_threadsafe() │ │ HandleOneMessage() │ │ Acceptor() │ │ RPC dispatch │ │ IO event monitoring │ └─────────────────────────┘ └──────────────────────────┘ ``` ### 消息协议 消息采用自定义二进制协议,基于 **msgpack** 序列化: ``` ┌────────────┬────────────┬──────────────────────┐ │ Header │ Flag │ Body │ │ 4 bytes │ 4 bytes │ N bytes │ │ (数据长度) │ (消息类型) │ (msgpack 序列化数据) │ └────────────┴────────────┴──────────────────────┘ ``` - **Header**(4 字节):大端序 int32,Body 部分的字节长度 - **Flag**(4 字节):大端序 int32,消息类型标识(`0` = 普通消息,`1` = RPC 调用) - **Body**(N 字节):msgpack 打包后的 dict 数据 ### RPC 机制 RPC 通过 `RpcWrapperBase` 的 `__getattr__` 实现链式调用语法: ```python # 客户端调用示例 self.m_LoginRpc.RRpcGetLoginMgr().OnGacLogin("cwl", "123") ``` 内部构建调用链:`[["RRpcGetLoginMgr", (,)], ["OnGacLogin", ("cwl", "123")]]` - `RRpcGetXxx()` 为路由节点,返回 `self` 实现链式调用 - 最终方法调用触发 `__call__`,通过连接发送 RPC 消息(含路由定义 + 调用链) --- ## 框架核心模块 ### App 生命周期 ```python class App: Init() # 初始化(配置读取、网络绑定) Loop() # 主循环(事件循环 / Tick 轮询) Unit() # 销毁(资源释放、连接关闭) ``` - `GasAppBase`(服务端基类):Init 中读取配置 → 绑定端口 → 启动 asyncio 事件循环 - `GacAppBase`(客户端基类):Init 中读取配置 → 连接登录服务 → Loop 中轮询网络事件 ### IOStream `IOStream` 封装单个 TCP 连接,负责: - 非阻塞读写(`_SimpleTryRead` / `_SimpleTrySend`) - 事件分发(根据 `EVENT_READ` / `EVENT_WRITE` 调用对应处理函数) - 消息解析(`TryParseMessage` 从字节缓冲区解析完整消息) - 消息发送(`TrySend` 将 dict 序列化为协议格式并写入发送缓冲区) ### TickMgr 定时器管理器,基于 `asyncio.ensure_future` + `asyncio.sleep` 实现: ```python TickMgr.RegisterTick("heartbeat", 5000, callback) # 每 5 秒循环执行 TickMgr.RegisterOnceTick("delayed", 3000, callback) # 3 秒后执行一次 TickMgr.UnRegisterTick("heartbeat") # 取消定时器 ``` ### Logger 单例日志系统,支持三个级别: - `info` — 绿色终端输出 - `warn` — 蓝色终端输出 - `error` — 红色终端输出 日志同时写入文件 `log/{appName}_{date}.log`。 ### IdManager 提供三种 ID 生成策略: - `IdManagerImplUuid` — 基于 UUID1 - `IdManagerImplIndex` — 自增整数 - `IdManagerImplRange` — 范围内循环自增 --- ## 快速开始 ### 环境要求 - Python 3.6+ - 依赖包:`msgpack` ### 安装依赖 ```bash pip install msgpack ``` ### 生成启动脚本 ```bash cd config python setup.py ``` 该命令根据 `server_config.json` 自动生成 `bat/` 目录下各进程的启动脚本和汇总脚本 `server.bat`。 ### 启动服务端 **方式一:一键启动全部服务** ```bash config\bat\server.bat ``` **方式二:单独启动某个服务** ```bash # 启动 login 服务 py -3 temple_server/login/main.py --name login # 启动 gas 服务 py -3 temple_server/gas/main.py --name gas1 ``` ### 启动客户端 ```bash py -3 temple_client/main.py ``` 客户端启动后会自动连接 `client_config.json` 中配置的 login 服务地址,并发送 RPC 登录请求。 --- ## 配置说明 ### client_config.json ```json { "login": { "ip": "127.0.0.1", "port": 4000 } } ``` 客户端通过此配置连接登录服务。 ### server_config.json ```json { "server_list": { "login": { "ip": "127.0.0.1", "port": 4000 }, "gam": { "ip": "127.0.0.1", "port": 4001 }, "gate1": { "ip": "127.0.0.1", "port": 5000 }, "gas1": { "ip": "127.0.0.1", "port": 5100 }, "ats1": { "ip": "127.0.0.1", "port": 5200 }, "gcc": { "ip": "127.0.0.1", "port": 5300 } } } ``` 每个服务进程在启动时通过 `--name` 参数匹配对应配置项读取 IP 和端口。 --- ## 扩展指南 ### 新增服务端进程 1. 在 `temple_server/` 下创建新目录(如 `myservice/`) 2. 创建 `main.py`(参考 `gas/main.py`),设置 `DIR_LEVEL` 和 argparse 3. 创建 `my_app.py`,继承 `GasAppBase` 并重写 `Init` / `HandleOneMessage` 4. 在 `server_config.json` 中添加对应配置 5. 在 `config/setup.py` 的 `g_dictStarterPath` 中注册新进程路径 6. 重新执行 `python setup.py` 生成启动脚本 ### 自定义消息处理 重写 `GasAppBase.HandleOneMessage(MsgObj)`: ```python def HandleOneMessage(self, MsgObj): nFlag = MsgObj.GetFlag() if nFlag == ERpcType.eRRpcCall: # 处理 RPC 调用 dictData = MsgObj.GetData() callChain = dictData.get(ERpcProtocol.eCallChain) # 路由到对应 Manager 执行方法 else: # 处理普通消息 MsgObj.ShowInfo() ``` --- ## 作者 **cwl** — 始于 2021-07-21