游戏服务端配置“热更”及“秒启动”终极方案(golang/ygluu/卢益贵)

游戏服务端配置“热更”及“秒启动”终极方案(golang/ygluu/卢益贵)

关键词:游戏微服务架构、游戏服务端热更、模块化解耦、golang

一、前言

众所周知,游戏服务端配置信息热更有几大问题(非lua架构):

1、因配置对象的指针被场景对象引用而导致热更复杂度提高

2、信息量大的配置表热更导致游戏卡顿、玩家闪断

3、一般重载后的配置信息仅影响重载后新创建的对应场景对象,不能影响已存在的场景对象

4、在高度解耦的模块化开发模式下导致热更复杂度提高

本示例代码将使用通用方法来演示在“高度解耦、模块化、模板化”的开发模式下对上述问题的解决方案,并提出游戏服务器秒启动的辅助方案。同时给出了完整示例代码及下载连接(见后)。

二、异步线程加载/重载方案

异步加载可以完美解决主线程重载大配置文件时可能引起游戏卡顿的现象。

图1

三、配置表碎片化方案

由多行电子表格或者xml格式自动导出一行对应一个ini文件的ini格式,重载时可大大减少重载时间。当然,采用异步重载的话可以不考虑此项方案。另外此项方案可应用于游戏秒启动,游戏启动时仅加载必要配置,场景等玩家相关的配置,在玩家登录进入场景以后创建场景对象时再在主线程里同步加载碎片化的配置,碎片化的配置加载时所需时间极短,玩家几乎没有太明显的卡断感知

当然秒启动还有一个重要方案:主从机方案,即采用主从机方式,在主机崩溃释放所占用端口号后,从机立即夺得相同端口的控制权,从机角色瞬间转变为主机角色

图2

四、指针间接引用方案

配置对象Cfg引用配置项Item,场景对象Map引用配置对象Cfg,每次Map使用配置信息时间接访问Cfg.Item。配置加载模块执行重载后,在主线程的回调中重置Cfg对象的Item字段,这样既不不影响主线程复杂的场景应用逻辑,又能让已存在的场景对象Map更新到最新的配置信息。

图3

五、重载通知方案

针对部分需要复制配置信息的场景对象,通过主线程同步执行重载回调函数OnReload,这样每个场景对象实例Map1~N都能及时更新到最新的配置信息。

图4

六、示例代码

(一)示例代码下载连接及运行效果

1、示例代码(golang)下载链接:

【免费】游戏服务端配置“热更”终极方案(golang/ygluu/卢益贵)资源-CSDN文库icon-default.png?t=N7T8https://download.csdn.net/download/GuestCode/88977874

2、示例代码运行效果

(二)示例代码结构及源码文件

1、示例代码模块化目录结构

2、主线程队列代码

主线程队列采用了匿名函数队列

package queue

/*******************************************************************************

        Author: Yigui Lu (卢益贵)
 Contact WX/QQ: 48092788
          Blog: https://blog.csdn.net/guestcode

   Creation 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: 48092788
          Blog: https://blog.csdn.net/guestcode

   Creation by: 2024-3-16

*******************************************************************************/

import (
	_ "cfgload/mapcfg"
	_ "cfgload/mapmgr"
	"cfgload/queue"
)

func main() {
	// 模拟游戏主线程
	for {
		// 匿名函数队列
		proc := <-queue.MainQ
		// 出列并执行
		proc()
	}
}

4、地图配置模块示例代码(加载/重载)

package mapcfg

/*******************************************************************************

        Author: Yigui Lu (卢益贵)
 Contact WX/QQ: 48092788
          Blog: https://blog.csdn.net/guestcode

   Creation by: 2024-3-16

*******************************************************************************/

// 本配置单元可以作为配置模板加载所有配置

import (
	"cfgload/queue"
	"io/ioutil"
	"log"
	"time"
)

const ModuleName = "地图配置模块"

// 地图配置项,一个对应一个
type Item struct {
	Name    string // 地图名
	ResName string // 地址资源名
	ResData []byte // 资源数据
	Attrs   []int  // 地图属性
}

// 外部类型:地图配置类
type Cfg struct {
	Item      *Item    // 真正的配置对象是Item
	LoadCount 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 != nil
		loadcount++
		if isReload {
			log.Println("地图配置模块(加载线程) => 正在重载......", loadcount)
		} else {
			log.Println("地图配置模块(加载线程) => 正在加载......")
		}

		var items []*Item

		for {
			// 模拟配置文件加载过程
			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 = item
					cfg.LoadCount = 1
					Cfgs[item.Name] = cfg
				} else {
					// 替换旧的配置
					cfg.Item = item
					cfg.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 <- true
		time.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: 48092788
          Blog: https://blog.csdn.net/guestcode

   Creation by: 2024-3-16

*******************************************************************************/

import (
	"cfgload/mapcfg"
	"cfgload/queue"
	"fmt"
	"log"
)

type Map struct {
	cfg   *mapcfg.Cfg
	Attrs []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,加载次数:%d"
	log.Println(fmt.Sprintf(txt, m.Name(), m.cfg.LoadCount))

	// csdn资源未修复此项bug
	m.Attrs = nil
	for _, 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的网络日志icon-default.png?t=N7T8https://www.codedump.info/post/20191206-gameserver-hot-refresh/

聊聊Golang游戏服务器的热更 | wudaijun's blogicon-default.png?t=N7T8https://wudaijun.com/2022/08/golang-gameserver-hotfix/

一种基于so的C/C++服务热更新方案_mob604756fea1c5的技术博客_51CTO博客icon-default.png?t=N7T8https://blog.51cto.com/u_15127648/4542189

游戏开发(九) 之 纯 lua 版 热更新 方案_纯lua的热更新方案-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zyxjx1314/article/details/106045843

lua游戏服务器热更新_lua热更函数但不修改变量-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/peter_teng/article/details/52751231

游戏开发中的热更新:一_热更新从服务器上下载的是什么文件-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_40695640/article/details/129463767

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/462831.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电脑那个部件坏了或者是哪个软件需要修复来看价钱

电脑维修价格表是多少&#xff1f; 价格取决于计算机的哪个部分损坏或哪个软件需要修复。 由于电脑中的部件非常多&#xff0c;而且会以各种奇怪的方式出现问题&#xff0c;下面我们就来看看具体的充电方法。 电脑维修价格表&#xff1a; 1. 重新安装系统。 安装XP系统通常需…

Tomcat Session 集群 ---------会话保持

一、 负载均衡、反向代理 环境搭建&#xff1a; nginx服务器192.168.246.7 tomcat 1服务器192.168.246.8 tomcat 2服务器192.168.246.9 7-1 nginx服务器搭建 [rootzzcentos1 ~]#systemctl stop firewalld [rootzzcentos1 ~]#setenforce 0 [rootzzcentos1 ~]#yum install …

Visual Studio配置libtorch(cuda安装一步到位)

Visual Studio配置libtorch visual Studio安装cuDNN安装CUDAToolkit安装libtorch下载Visual Studio配置libtorch(cuda版本配置) visual Studio安装 visual Studio点击安装 具体的安装和配置过程这里就不进行细讲了&#xff0c;可以参考我这篇博客Visual Studio配置OpenCV(保姆…

openCV实现拖拽虚拟方块

一、项目效果&#xff1a; 二、核心流程&#xff1a; openCV读取视频流、在每一帧图片上画一个矩形。使用mediapipe获取手指关键点坐标。根据手指坐标位置和矩形的坐标位置&#xff0c;判断手指点是否在矩形上&#xff0c;如果在则矩形跟随手指移动。 三、代码流程&#xff1…

基于SpringBoot框架实现的B2B平台的医疗病历交互系统

采用技术 基于SpringBoot框架实现的B2B平台的医疗病历交互系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 管理员角色 医院管理 医院注册 医院文…

C语言—打印如图矩阵

输出矩阵 在一个二维数组中形成并输出如下矩阵: #include <stdio.h> main() { int i,j,a[5][5];for(i0;i<4;i)for(j0;j<4;j)if(i<j) a[i][j]1;else a[i][j]i-j1;for(i0;i<4;i){ for(j0;j<4;j)printf("%d ",a[i][j]);printf("…

介绍一款鼠标无边界软件

"Mouse without Borders" 是一款由微软开发的免费工具&#xff0c;旨在帮助用户在多台计算机之间实现无缝的鼠标和键盘共享。通过 Mouse without Borders&#xff0c;用户可以在一个主控制台上控制多台计算机&#xff0c;就像操作一个大型虚拟桌面一样。 这个工具可…

zookeeper基础学习之六: zookeeper java客户端curator

简介 Curator是Netflix公司开源的一套zookeeper客户端框架&#xff0c;解决了很多Zookeeper客户端非常底层的细节开发工作&#xff0c;包括连接重连、反复注册Watcher和NodeExistsException异常等等。Patrixck Hunt&#xff08;Zookeeper&#xff09;以一句“Guava is to Java…

数字IC实践项目(9)—SNN加速器的设计和实现(tiny_ODIN)

数字IC实践项目&#xff08;9&#xff09;—基于Verilog的SNN加速器 写在前面的话项目整体框图完整电路框图 项目简介和学习目的软件环境要求 Wave&CoverageTiming&#xff0c;Area & Power总结 写在前面的话 项目介绍&#xff1a; SNN硬件加速器是一种专为脉冲神经网…

nanodet笔记

nanodet简单笔记 这里简单记录下nanodet的结构. 网络stride8,16,32的feature 经过共享卷积处理后cat在一起再transpose,直接产生shape为(B, n_grid, n_class8*4),其中B为batchsize, n_grid为feature map上点的个数, 4:表示top,bottom, left, right 4个值,每个值使用8个bin的概…

QT C++ QButtonGroup应用

//QT 中&#xff0c;按钮数量比较少&#xff0c;可以分别用各按钮的信号和槽处理。 //当按钮数量较多时&#xff0c;用QButtonGroup可以实现共用一个槽函数&#xff0c;批量处理&#xff0c;减少垃圾代码&#xff0c; //减少出错。 //开发平台&#xff1a;win10QT6.2.4 MSVC…

《计算机视觉中的深度学习》之目标检测算法原理

参考&#xff1a;《计算机视觉中的深度学习》 概述 目标检测的挑战&#xff1a; 减少目标定位的准确度减少背景干扰提高目标定位的准确度 目标检测系统常用评价指标&#xff1a;检测速度和精度 提高精度&#xff1a;有效排除背景&#xff0c;光照和噪声的影响 提高检测速度…

GitHub Actions持续部署

一、概述 1.1Github Action介绍 什么是Github Action ? GitHub Actions是GitHub提供的CI/CD&#xff08;持续集成/持续部署&#xff09;服务。它允许你在GitHub仓库中自动化、定制和执行你的软件开发工作流。你可以发现、创建和分享用于执行任何你想要的工作的操作&#xff0…

媒体发稿:澳门媒体发稿7个流程

推广平台澳门是一个重要的度假旅游娱乐终点&#xff0c;都是媒体领域热议的话题。对于澳门的媒体发稿营销推广要求&#xff0c;大家提供了一个简单易用的套餐系统软件&#xff0c;帮助大家在澳门媒体上发表推广文章。下面我们就根据7个阶段&#xff0c;详解构建这一套餐推广平台…

Vue+SpringBoot打造教学过程管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 教师端2.2 学生端2.3 微信小程序端2.3.1 教师功能如下2.3.2 学生功能如下 三、系统展示 四、核心代码4.1 查询签到4.2 签到4.3 查询任务4.4 查询课程4.5 生成课程成绩 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVu…

CentOS Stream9更改ip地址,网关(设置静态ip)

使用grep命令查询ens160文件所在的文件夹处 grep -rnw /etc -e ens160 然后用vi命令打开文件进行修改 vi /etc/NetworkManager/system-connections/ens160.nmconnection 配置&#xff1a; 假设将ip地址改为192.168.200.130 [connection] idens33 uuid0050f214-01a7-395e-…

数据分析-Pandas雷达图的多维数据可视化

数据分析-Pandas雷达图的多维数据可视化 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表…

【jeecgboot】微服务实战LISM

目录 一、服务解决方案-Spring Cloud Alibaba1.1选用原因&#xff08;基于Spring Cloud Alibaba的试用场景&#xff09;1.2 核心组件使用前期规划 部署 nacos部署 mino使用JavaFreemarker模板引擎&#xff0c;根据XML模板文件生成Word文档使用JavaFlowable 工作流引擎前端 -vue…

maven工程,未被idea识别为maven工程怎么办?

示例&#xff1a;以下工程的pom文件图标不是一个蓝色的m&#xff0c;所以未被识别为maven工程。 解决办法&#xff1a;打开pom.xml文件—>右键—>add as maven project 问题解决&#xff1a;

服务器机器学习环境搭建(包括AanConda的安装和Pytorch的安装)

服务器机器学习环境搭建 1 服务器与用户 在学校中&#xff0c;我们在学校中是以用户的身份进行访问学校的服务器的。整体框架大致如下&#xff1a; 我们与root用户共享服务器的一些资源&#xff0c;比如显卡驱动&#xff0c;Cuda以及一些其他的公共软件。 一般情况下&#…