Go游戏服务器热更新

1.为什么需要做代码热更新?

  • 什么是代码热更新
    所谓代码热更一般是指服务程序在不停止服务运行的情况下动态替换其中部分代码对原有逻辑进行修正或者新增功能代码。
  • 为什么需要代码热更新
    服务程序一般需要为应用程序提供长时间724小时不间断的服务。
    不停止服务程序的运行的情况下动态修改程序的行为逻辑。
    游戏服务器频繁停服维护非常不友好。*

2.游戏服务器热更主流解决方案?

代码热更的基本原理

我们可以用一个公式来简单概括服务程序。服务程序=数据结构(数据管理)+算法(这里说的算法指代程序中对于数据的计算处理相关的部分)。大部分时候代码热更新只能热更算法的部分,数据部分是非常难热更的或者说不建议热更的。举个例子:

上述代码可以进行热更的部分是ChangeGameData方法。试想一下如果修改了GameData数据结构,删除了D字段新增了E字段,那么此时数据结构与程序内存中的GameData对象的内存结构是不匹配的,那么对于字段的操作也不能达到预想的效果。到这里我们热更的目标很明确了,就是如何能够动态的替换掉ChangeGameData这类的对于数据的计算的函数?

代码热更方案:

基本原理

  1. 动态库替换
  2. 编译型语言+(运行时编译器(luajit/cpython)+动态解析内嵌脚本)

实践方案

  1. c/c++动态库(.so或者.dll)替换
  2. c/c++内嵌lua
  3. c/c++内嵌python
  4. node.js
  5. golang+plugin

3.Go游戏服务器如何做热更新

前面实践方案中已经剧透了Go语言中实现代码热更新的方式是采用plugin包加载动态库的方式。go编译器支持将一个go程序编译成一个插件(plugin)。plugin的基本原理类似于c/c++加载动态库的方式。

插件程序构建

  • 程序目录结构
  • 代码解析-插件代码(plugintest/plugin/main.go)

  • 代码解析-宿主程序代码(plugintest/main.go)

  • 编译-plugin编译

在windows上编译plugin时发现windows上不支持plugin模式,我们来看看go官方的说明。
pkg_plugin_doc

  • 编译-宿主程序编译

    上面的示例已经示范了如何编写插件代码,如何编译插件代码,如何使用plugin加载插件代码,如何调用plugin代码的全部流程了。剩下的就是对plugin的封装以及梳理加载、重载的流程了。

    插件程序避坑指南-plugin都有哪些坑?

同一个插件只会加载一次(宿主程序的整个生命周期)

同一个插件必须在不同的路径名下进行编译否则依然被认为是同一个插件

同一个插件与宿主程序公用的公共代码库必须没有变更,并且在同一个环境下编译才能进行加载

①为了验证这个问题我们新增一个公共package common

②修改宿主程序和plugin都引用package common

②修改package common的代码

③重新编译插件->plugin2.so

④重新执行

如何避免/解决这些坑呢?

所谓的这些坑本质上并不一定是坑只是我们对它的基本原理理解的没有那么清晰。分析出这些坑的原因后,其实很容易避免。我们只需要遵循它的基本准则就可以避免了。比方说上面提到的一些问题,只需要每次编译插件代码时将插件目录复制到一个临时目录进行编译即可。如果插件内部有多个目录包含多个package那么在将代码复制到临时目录后需要动态的将代码中引用这些package的路径和名称也做相应的修改即可。

4.Go游戏服务器代码热更工程应用

下面是一个在正式项目中应用plugin进行热更新的项目工程目录结构。

如何在正式项目中合理的应用plugin进行热更新的方法可以参考下图:
draw_horse

测试代码在这里

  plugintest.zip (1.3 KB, 215 次)

One Comment

  • 阿凡达123

    也可以看看这个:https://github.com/edwingeng/hotswap

    Hotswap为go语言代码热更提供了一套相当完整的解决方案,热更过程不会中断或阻塞任何执行中的函数,更不会重启服务器。此方案建立在go语言的plugin机制之上。

发表评论

电子邮件地址不会被公开。 必填项已用*标注