Skynet搭建的棋牌服务器实现部分Lua业务热更新

本文介绍在Skynet框架下,如何实现棋牌服务器的部分Lua业务模块进行无状态的平滑热更新,涵盖agent(客户端代理)和roomkeeper(游戏房间管理)两大模块的热更新策略,以及解决热更新过程中可能出现的状态不一致问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

热更新最大的难点是状态同步,所以无状态的热更新是最容易实现的,本篇幅的热更新方案的约束条件就是不涉及状态同步的模块。

挑选出业务内善变的模块做热更新方案,从而提高开发效率,本篇幅是总结一下我工作当中的棋牌服务器是如何实现部分Lua业务进行平滑热更新的。

棋牌服务器是采用云风的开源服务器框架Skynet搭建的,具体的业务用Lua脚本开发。

目前项目实现了agent(客户端代理)和roomkeeper(游戏房间管理)2个大模块的热更新,每个agent对应一条连接的客户端,代理接收客户端的所有请求,大厅相关的业务拓展几乎是在这个模块上开发的,比如邮件、签到、充值等等;而roomkeeper则是管理玩家游戏的虚拟房间。

热更新方案:

单独开启一个服务通过忙等待的方式定时检测热更新配置文件是否被修改过,如果文件被修改,那么重新加载该配置文件,根据最新的配置内容来决定更新agent还是roomkeeper模块,亦或者是都更新。如果有模块需要更新,那么先调用skynet.clearcache(为什么调用clearcache请参考云风的这篇博客《在不同的 lua vm 间共享 Proto》),然后向相关服务发送热更新请求,相关服务在接收到请求后以合适的策略进行热更新。

agent服务存储的玩家状态不在热更新方案范畴,业务逻辑的相关处理模块会加入热更新。

agent模块热更新是通过agent池向所有agent服务发送hotupdate请求的方式,agent服务接收到消息后,在skynet.queue执行队列里获得执行权时,把需要更新的业务模块通过package.loaded[module_name] = nil措施清理掉缓存,然后重新require,这样就完成了热更新。

在skynet框架中,如果agent服务在处理某请求过程当中发生了阻塞调用(比如skynet.call/cluster.call),那么当前coroutine会被挂起,并且该agent服务照样可以处理其他请求。如果阻塞调用的下一行调用了一个全局方法globalFunc,假设服务这时收到热更新请求,而热更新的内容包括删除globalFunc方法,那么当被挂起的coroutine被唤醒继续执行下一行代码(也就是调用globalFunc方法),因为globalFunc方法在热更新的时候删除了,找不到就会抛出异常。

为了避免这类问题,我们引用了skynet.queue,把agent服务接收的消息的响应处理函数都压入skynet.queue执行队列,这样每次调用它都能得到一个新的临界区,即上一个函数处理没有返回,下一个函数也是不可能执行的,这样就能保证消息处理函数执行的时序性。

roomkeeper模块热更新则是使用一种灰度平滑切换的方式进行。我们采用的是用一个数组来管理两个room服务对象(每个room对象类似于一个酒店,游戏虚拟房间由room对象创建,类似于在就酒店开房),常规情况下,其中一个room服务对外工作,另一个则处于空闲状态,当roomkeeper服务接收到hotupdate请求时,首先把空闲状态的room服务杀死,通过package.loaded[module_name] = nil措施清理掉模块缓存,再新创建一个room服务加入到这个数组中(因为模块代码已经更新,并且缓存里的旧模块也清理掉了,新创建的服务肯定是用最新的模块代码加载的),并且在room服务里预生成一批roomobject对象(roomobject是游戏虚拟房间对象,房间解散了就释放),然后把新创建的room服务切换为工作状态,而原本工作状态的room服务则设置为关闭状态,玩家需要房间进行游戏都是从处于工作状态的room服务里获取roomobject,如果room服务预生成的那批roomobject不够用,则新建一批roomobject,然后分配一个空闲的roomobject给玩家打牌。

roomkeeper热更新处理流程是通过定时器周期性检测所有room服务是否更新完毕,处于关闭状态的room服务如果有roomobject还有牌局正在进行,不能直接杀死room服务,必须在room服务所管理的所有roomobject里都没有真实玩家的情况下才能杀死重新生成(因为前面切换为最新模块的room服务已经处于工作状态了,所有后面切换完成的room服务会将关闭状态修改为空闲状态)。

待两个room服务都完成了从旧模块切换为新模块的过渡工作,热更新就完成了。

 

<think>好的,用户现在想了解如何基于Skynet框架开发服务器,并且需要详细的项目内容和框架下载地址,特别是要能在Windows系统上部署。之前用户已经询问过如何利用开源框架开发MMORPG服务端,我给出了包括Skynet在内的多个框架的对比。现在用户可能已经决定选择Skynet,因此需要更具体的指导。 首先,我需要确认Skynet在Windows上的支持情况。Skynet原本是为Linux设计的,但可能有社区提供的Windows版本或者通过Cygwin、WSL等方式运行。用户可能需要详细的步骤来在Windows上配置环境,特别是考虑到用户可能对Linux环境不熟悉,所以需要简化步骤。 接下来,用户需要详细的项目内容,可能包括项目结构、核心模块的代码示例、配置方法等。需要确保提供的示例足够清晰,特别是Lua脚本部分,因为Skynet主要使用Lua进行开发。同时,用户可能需要知道如何启动服务器、处理网络请求、管理服务等基本操作。 另外,用户可能需要下载链接,确保提供的链接是官方或可靠的来源。可能需要指导用户如何通过GitHub获取源码,并解决可能遇到的编译问题,尤其是在Windows环境下可能需要依赖的库或工具,如Make、GCC等,可能需要使用MinGW或MSYS2。 还需要考虑用户的实际应用场景,比如开发MMORPG需要处理的高并发、网络同步、数据库集成等问题,可能需要提供相应的解决方案或推荐工具。例如,推荐使用Protobuf进行协议编码,MySQL或Redis作为数据库,以及负载测试工具。 用户可能没有明确提到的潜在需求包括热更新机制、分布式部署、调试工具等,这些在游戏服务器开发中也很重要,可能需要简要提及或提供参考资料。此外,考虑到用户可能在开发过程中遇到问题,应推荐一些社区资源或文档链接,方便用户自行查找解决方案。 需要确保回答的结构清晰,分步骤指导,从环境搭建到核心模块开发,再到部署测试,逐步推进。同时,代码示例和配置文件要准确,避免错误导致用户操作失败。最后,提醒用户注意Skynet在Windows上的性能可能不如Linux,建议生产环境使用Linux服务器,但开发阶段可以在Windows上进行。</think>以下是基于Skynet框架开发游戏服务端的完整指南,包含Windows部署方案、项目结构示例和关键技术实现: --- ### 一、Skynet框架基础信息 - **官方地址**:https://github.com/cloudwu/skynet - **Windows移植版**:https://github.com/dpull/skynet-windows (推荐) - **核心特性**: - 单进程多协程Actor模型 - Lua/C混合开发模式 - 支持热更新和服务监控 - 自带网络库和定时器 --- ### 二、Windows开发环境搭建 #### 1. 依赖工具安装 - **Visual Studio 2022** (需勾选C++开发组件) - **LuaForWindows**:https://github.com/rjpcomputing/luaforwindows - **Git Bash**:用于执行shell脚本 #### 2. 框架编译 ```bash # 克隆Windows移植版 git clone --recursive https://github.com/dpull/skynet-windows # 修改编译配置(使用VS2022) 修改 skynet-windows/3rd/jemalloc/Makefile.vc 中的 MSVC_VERSION=14.3 # 编译 cd skynet-windows make -f Makefile.win ``` #### 3. 目录结构说明 ``` skynet-windows/ ├── bin/ # 可执行文件 ├── lualib/ # Lua标准库 ├── service/ # 系统服务 ├── lua/ # Lua主程序 ├── examples/ # 示例代码 └── config.win # Windows专用配置 ``` --- ### 三、基础服务端项目示例 #### 1. 配置文件(config.win) ```lua thread = 8 logger = nil harbor = 0 start = "main" # 主入口脚本 bootstrap = "snlua bootstrap" lualoader = "lua-loader.lua" preload = "./preload.lua" # 网络配置 cpath = "./cservice/?.dll" lua_path = "./lualib/?.lua;./examples/?.lua" lua_cpath = "./luaclib/?.dll" ``` #### 2. 主入口脚本(main.lua) ```lua local skynet = require "skynet" skynet.start(function() -- 启动日志服务 local logservice = skynet.newservice("logservice") -- 启动网关服务 local gateway = skynet.newservice("gateway") skynet.call(gateway, "lua", "start", { port = 8888, maxclient = 65535, }) -- 启动玩家管理服务 skynet.newservice("playermgr") skynet.error("Server startup complete") end) ``` #### 3. 网关服务示例(service/gateway.lua) ```lua local skynet = require "skynet" local socket = require "skynet.socket" local CMD = {} function CMD.start(conf) local listenfd = socket.listen("0.0.0.0", conf.port) socket.start(listenfd, function(fd, addr) skynet.error("New client from:"..addr) skynet.newservice("client", fd) end) end skynet.start(function() skynet.dispatch("lua", function(_,_, cmd, ...) local f = CMD[cmd] skynet.ret(skynet.pack(f(...))) end) end) ``` --- ### 四、MMORPG核心模块实现 #### 1. 网络协议处理(Protobuf集成) ```lua -- 加载协议文件 local protobuf = require "protobuf" protobuf.register_file("./protocol/login.pb") -- 接收客户端消息示例 function client.receive(fd) local str = socket.read(fd) local msg = protobuf.decode("LoginReq", str) skynet.error("Received login:", msg.account) end ``` #### 2. 玩家数据管理 ```lua -- 玩家服务(service/player.lua) local db = require "db_mysql" function CMD.login(account) local player = db.query("SELECT * FROM users WHERE account=?", account) if not player then player = create_new_player(account) end return player end function create_new_player(account) local player = { account = account, level = 1, last_login = os.time() } db.execute("INSERT INTO users VALUES (?,?,?)", account, player.level, player.last_login) return player end ``` #### 3. AOI同步模块 ```lua -- 九宫格视野管理 local aoi = require "aoi" function CMD.move(player, x, y) local old_grid = aoi.get_grid(player.x, player.y) local new_grid = aoi.get_grid(x, y) if old_grid ~= new_grid then aoi.leave_grid(player, old_grid) aoi.enter_grid(player, new_grid) end -- 广播移动消息 broadcast_to_viewers(player, { type = "move", target = player.id, x = x, y = y }) end ``` --- ### 五、Windows部署注意事项 1. **性能优化**: - 使用`jemalloc`内存分配器(已包含在移植版) - 关闭杀毒软件实时监控 - 设置进程优先级为HIGH 2. **端口配置**: ```lua -- 在config.win中增加 socket_max = 1024 # 最大连接数 socket_rcvbuf = 128 # 接收缓冲区(KB) socket_sndbuf = 256 # 发送缓冲区(KB) ``` 3. **热更新方案**: ```lua -- 热加载新代码 skynet.hotfix("service.gateway", function(oldtbl) local newtbl = require "service.gateway_v2" for k,v in pairs(newtbl) do oldtbl[k] = v end end) ``` --- ### 六、开发工具链推荐 1. **调试工具**: - **Wireshark**:网络协议分析 - **LuaPanda**:VSCode调试插件 - **DBGp**:远程调试协议 2. **压力测试**: ```bash # 使用skynet自带的testclient ./skynet.exe config.win testclient 5000 # 模拟5000个连接 ``` 3. **监控面板**: ```lua -- 安装skynet-monitor local monitor = require "skynet.monitor" monitor.start({ port = 8889, # Web监控端口 refresh = 5 # 刷新间隔(秒) }) ``` --- ### 七、学习资源 1. **官方文档**: - Skynet设计原理:https://github.com/cloudwu/skynet/wiki - API参考手册:https://github.com/cloudwu/skynet/wiki/APIList 2. **推荐书籍**: - 《Skynet游戏服务器开发实战》 - 《Lua程序设计(第4版)》 3. **实战项目案例**: - 简易棋牌游戏:https://github.com/cloudwu/skynetdemo - MMORPG Demo:https://github.com/kbengine/skynet_mmo --- 按照上述方案,可在Windows上搭建支持5000同时在线的MMORPG服务端。建议开发环境使用Windows + WSL2组合,生产环境部署到Linux服务器。关键点在于合理划分Skynet服务职责,做好消息队列的负载均衡。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值