大型网站建站公司,网站优化用户体验,wordpress 插件 推荐,网站后台 网站页面没有显示游戏服务端配置“热更”及“秒启动”终极方案
ygluu 卢益贵
关键词#xff1a;游戏微服务架构、游戏服务端热更、模块化解耦、golang
目录
一、前言
二、异步线程加载/重载方案
三、配置表碎片化方案
四、指针间接引用
五、重载通知
六、示例代码
七、相关连接 一、…游戏服务端配置“热更”及“秒启动”终极方案
ygluu 卢益贵
关键词游戏微服务架构、游戏服务端热更、模块化解耦、golang
目录
一、前言
二、异步线程加载/重载方案
三、配置表碎片化方案
四、指针间接引用
五、重载通知
六、示例代码
七、相关连接 一、前言
众所周知游戏服务端配置信息热更有几大问题非lua架构
1、因配置对象的指针被场景对象引用而导致热更复杂度提高
2、信息量大的配置表热更导致游戏卡顿、玩家闪断
3、一般重载后的配置信息仅影响重载后新创建的对应场景对象不能影响已存在的场景对象
4、在高度解耦的模块化开发模式下导致热更复杂度提高
本示例代码将使用常用通用的方法来演示在“高度解耦、模块化、模板化”的开发模式下对上述问题的解决方案并提出游戏服务器秒启动的辅助方案。同时给出了两种方案的完整示例代码下载连接见后。
二、异步线程加载/重载方案
异步加载可以完美解决主线程重载大配置文件时可能引起游戏卡顿的现象。见下载连接1。 图1
三、配置表碎片化方案
由多行电子表格或者xml格式自动导出一行对应一个ini文件的ini格式重载时可大大减少重载时间。另外此项方案可应用于游戏秒启动游戏启动时仅加载必要配置场景等玩家相关的配置在玩家登录进入场景以后创建场景对象时再在主线程里同步加载碎片化的配置碎片化的配置加载时所需时间极短玩家几乎没有太明显的卡断感知。见下载连接2。
当然秒启动还有一个重要方案主从机方案即采用主从机方式在主机崩溃释放所占用端口号后从机立即夺得相同端口的控制权从机角色瞬间转变为主机角色。 图2
四、指针间接引用
配置对象Cfg引用配置项Item(真正记载配置信息的地方)场景对象Map引用配置对象Cfg每次Map使用配置信息时间接访问Cfg.Item。配置加载模块执行重载后在主线程的回调中重置Cfg对象的Item字段这样既不不影响主线程复杂的场景应用逻辑又能让已存在的场景对象Map更新到最新的配置信息。 图3
五、重载通知
针对部分需要复制配置信息的场景对象通过主线程同步执行重载回调函数OnReload这样每个场景对象实例Map1~N都能及时更新到最新的配置信息。 图4
六、示例代码
一示例代码下载连接及运行效果
1、示例代码golang下载链接
线程异步加载方案示例代码1
游戏服务端配置“热更”终极方案https://download.csdn.net/download/GuestCode/88977874配置表碎片化加载方案示例代码2
游戏服务端配置“热更”及“秒启动”终极方案https://download.csdn.net/download/GuestCode/88979391
2、示例代码运行效果
重载后会通知所有场景实例更新最新的配置信息。 图5 线程异步加载效果 图6 配置表碎片化重载效果
二示例代码结构及源码文件
1、示例代码模块化目录结构 图7
2、主线程队列代码异步加载方案
主线程队列采用了匿名函数队列。
package queue/*******************************************************************************Author: Yigui Lu (卢益贵)Contact WX/QQ: 48092788Blog: https://blog.csdn.net/guestcodeCreation by: 2024-3-16*******************************************************************************/// 主线程队列匿名函数仅主线程调用无需同步
var MainQ make(chan func())// 主线程事件响应函数列表
var events make(map[string]func())// 触发主线程事件
func TriggerEvent(name string) {MainQ - func() {on : events[name]if on ! nil {on()}}
}// 设置事件监听在主线程中执行onEvent
func ListenEvent(name string, onEvent func()) {events[name] onEvent
}3、主线程代码异步加载方案
package main/*******************************************************************************Author: Yigui Lu (卢益贵)Contact WX/QQ: 48092788Blog: https://blog.csdn.net/guestcodeCreation by: 2024-3-16*******************************************************************************/import (_ cfgload/mapcfg_ cfgload/mapmgrcfgload/queue
)func main() {// 模拟游戏主线程for {// 匿名函数队列proc : -queue.MainQ// 出列并执行proc()}
}4、地图配置模块示例代码异步加载方案
加载和重载共一份逻辑代码。
package mapcfg/*******************************************************************************Author: Yigui Lu (卢益贵)Contact WX/QQ: 48092788Blog: https://blog.csdn.net/guestcodeCreation by: 2024-3-16*******************************************************************************/// 本配置单元可以作为配置模板加载所有配置import (cfgload/queueio/ioutillogtime
)const ModuleName 地图配置模块// 地图配置项一个对应一个
type Item struct {Name string // 地图名ResName string // 地址资源名ResData []byte // 资源数据Attrs []int // 地图属性
}// 外部类型地图配置类
type Cfg struct {Item *Item // 真正的配置对象是ItemLoadCount int // 加载次数演示用onReloads []func() // 触发重载回调部分场景需求用
}func (c *Cfg) AddOnReload(onReload func()) {c.onReloads append(c.onReloads, onReload)
}// 配置表
type cfgs map[string]*Cfg// 配置表全局变量/外部接口仅主线程调用无需同步
var Cfgs cfgs// 配置异步加载协程
var loadcount int// 异步加载/重载通用函数无需两者区分而额外编写代码
func loadFunc(ch chan bool) {for {// 等待加载通知-ch// 以下在加载线程中执行 --isReload : Cfgs ! nilloadcountif isReload {log.Println(地图配置模块加载线程 正在重载......, loadcount)} else {log.Println(地图配置模块加载线程 正在加载......)}var items []*Itemfor {// 模拟配置文件加载过程item : Item{Name: 南天门,ResName: res001.map,}item.ResData, _ ioutil.ReadFile(item.ResName)items append(items, item)break // 仅加载一行}// -- 以上在加载线程中执行// 匿名函数将在主线程执行达到线程同步目的onModuleLoadedInMain : func() {if Cfgs nil {// 首次加载Cfgs make(cfgs)}for _, item : range items {cfg : Cfgs[item.Name]if cfg nil {// 新增配置cfg Cfg{}cfg.Item itemcfg.LoadCount 1Cfgs[item.Name] cfg} else {// 替换旧的配置cfg.Item itemcfg.LoadCount// 在主线程调用重载通知回调只有部分场景需求才需要通知for _, onReload : range cfg.onReloads {onReload()}}}}if isReload {log.Println(地图配置模块加载线程 重载完毕, loadcount)} else {log.Println(地图配置模块加载线程 加载完毕)}// 给主消息队列压栈// 主线程会调用onModuleLoadedInMain执行达到线程同步目的queue.MainQ - onModuleLoadedInMain// 首次加载触发事件告诉主线程模块加载完毕if !isReload {queue.TriggerEvent(ModuleName)}}
}func reloadTrigger(ch chan bool) {// 模拟5秒钟触发一次重载请求for {ch - truetime.Sleep(time.Second * 5)}
}func init() {ch : make(chan bool)go loadFunc(ch)go reloadTrigger(ch)
}5、地图管理模块示例代码异步加载方案
package mapmgr/*******************************************************************************Author: Yigui Lu (卢益贵)Contact WX/QQ: 48092788Blog: https://blog.csdn.net/guestcodeCreation by: 2024-3-16*******************************************************************************/import (cfgload/mapcfgcfgload/queuefmtlog
)type Map struct {cfg *mapcfg.CfgAttrs []int
}// 间接引用转为直接引用Map.CfgItem
func (m *Map) CfgItem() *mapcfg.Item {return m.cfg.Item
}// 直接引用配置对象字段示例
func (m *Map) Name() string {return m.cfg.Item.Name
}// 复制配置信息示例模拟部分需求场景无需求则可以省略
func (m *Map) onReloadCopyInfo() {const txt 地图管理模块主线程 复制配置信息地图名称%s加载次数%dlog.Println(fmt.Sprintf(txt, m.Name(), m.cfg.LoadCount))// csdn资源未修复此项bugm.Attrs nilfor _, attr : range m.CfgItem().Attrs {m.Attrs append(m.Attrs, attr)}
}var Maps make(map[string]*Map)func createMap() {log.Println(地图管理模块主线程 正在创建地图......)for _, cfg : range mapcfg.Cfgs {mp : Map{cfg: cfg,}// 无复制配置信息需求时可以省略此步骤cfg.AddOnReload(mp.onReloadCopyInfo)// 为避免代码冗余首次加载自己调用onReloadCopyInfo复制配置信息mp.onReloadCopyInfo()}log.Println(地图管理模块主线程 创建地图完毕)
}func init() {// 待地图配置首次加载完毕后创建地图queue.ListenEvent(mapcfg.ModuleName, createMap)
}七、相关连接
对比脚本型和编译型游戏服务器的热更新方案 - codedump的网络日志https://www.codedump.info/post/20191206-gameserver-hot-refresh/
聊聊Golang游戏服务器的热更 | wudaijuns bloghttps://wudaijun.com/2022/08/golang-gameserver-hotfix/
一种基于so的C/C服务热更新方案_mob604756fea1c5的技术博客_51CTO博客https://blog.51cto.com/u_15127648/4542189
游戏开发(九) 之 纯 lua 版 热更新 方案_纯lua的热更新方案-CSDN博客https://blog.csdn.net/zyxjx1314/article/details/106045843
lua游戏服务器热更新_lua热更函数但不修改变量-CSDN博客https://blog.csdn.net/peter_teng/article/details/52751231
游戏开发中的热更新一_热更新从服务器上下载的是什么文件-CSDN博客https://blog.csdn.net/weixin_40695640/article/details/129463767