解读golang plugin热更新尝试

作者:呵大官人 时间:2024-05-22 10:09:28 

当我们在使用php开发的时候,基本不需要关心热更新这件事的,因为PHP本身已经帮我处理好了,只需要提交代码,PHP重新解释一遍即可。而go则是静态语言,编译后得到的是直接被机器执行的,所有代码已经翻译成相对应的机器指令并且在运行时已经加载到内存,不能动态更新。那么如果想热更新就成了件麻烦的事,但是作为后端开发人员,很渴望支持这种功能,毕竟在线上能新增功能、修复bug客户端完全无感知是多么完美的事。

本文暂不讨论http这种无状态服务更新,网上能搜索到很多文章关于如何利用fd继承实现优雅重启。这里主要讨论使用golang 1.8新增的plugin来实现业务的更新,并且业务是类似游戏的有状态服务。官方文档中对plugin的描述比较简单,他可以动态的加载so和执行导出的方法,并且仅仅提供了两个方法:打开模块和提取符号,甚至连关闭都没有(-_-)。

一个程序包含两部分:数据和算法,那么既然是有状态服务,数据部分肯定不能动,那么热更就只能动算法部分了。这时我们需要一个容器,将这两部分隔离开,一方面是存储数据,另一方面要动态加载so。隔离了数据和算法,只要数据存在,我们就可以随意更新算法了。在开始编码之前,要先解决几个问题:

1、同一个so文件只会被打开一次

2、每个so有一个pluginpath用来标识是否重复,如果两个so文件不一样,但pluginpath一样还是会报错

3、不同so文件定义的结构体不能使用类型断言进行转换

对于上面的问题,有如下解决方案:

1、每次生成的so带一个版本号比如game.1001.so

2、编译的时候新增--ldflags="-pluginpath=xxx"参数

3、使用unsafe进行转换(下面还会有注意事项)

 代码地址:https://github.com/scgywx/myplugin

1、编译engine,这就是我们上面说的容器,他负责数据存储和so的加载与执行。


sh build.sh

2、编译第1个版本so(注意后面有个参数)


sh build_so.sh 1

3、将src/logic/main.go里面的modelVersion和modelName分别改成1002和game2(这里主要是测试两个版本的内容区别)

4、编译第2个版本so


sh build_so.sh 2

5、运行容器


./engine

6、浏览器输入127.0.0.1:12345/hello,会看到如下显示(这是使用的第一个版本so)

hello test, this is golang plugin test!, version=1001, name=game1, oldversion=0, oldName=


7、浏览器输入127.0.0.1:12345/load?name=plugin2.so(这里输出done,就说明加载so成功了)

8、再次输入127.0.0.1:12345/hello,会看到如下显示。

hello test, this is golang plugin test!, version=1002, name=game2, oldversion=1001, oldName=game1

 

到这里,我们的热更新效果已经达成,但是还是有一些限制

1、每个so不能单独保存数据,因为当另一个so加载后,前面so的数据是没办法访问到,并且由于so不能被关闭,可能会出现多个so引用同一个变量,gc没办法释放,所以需要透过容器来共享数据,那么我们就不能在模块内使用全局变量来保存数据。

2、go里面两个类型即使一样,也不能直接转换,所以两个so内定义的结构体也不能直接转换,要使用unsafe.Pointer来进行强转(见src/logic/main.go),既然是强转,那么两个版本的so使用的结构体定义就不能有区别,否则转换后数据可能会出现异常,也就是说热更新不能修改结构体。

 本文只是技术尝试,没有线上验证,还有多少坑还不知道,热更新不是必须,如若支持,便是好事。。

来源:https://my.oschina.net/scgywx/blog/1796358

标签:golang,plugin,热更新
0
投稿

猜你喜欢

  • Python 查看文件的编码格式方法

    2021-11-01 10:50:39
  • 精通Javascript系列之数据类型 字符串

    2024-04-17 09:49:59
  • Pygame Rect区域位置的使用(图文)

    2023-08-14 05:27:48
  • 使用Tkinter制作信息提示框

    2023-12-15 02:13:30
  • python定时任务sched库用法简单实例

    2023-11-07 07:26:14
  • Mysql查询去空格的多种方法汇总

    2024-01-25 02:27:18
  • Mysql误删数据解决方案及kill语句原理

    2024-01-26 20:22:15
  • 解析:校对确定 较为复杂的一些特殊情况

    2008-12-17 16:54:00
  • asp.net连接数据库读取数据示例分享

    2024-01-23 11:45:19
  • 利用python+ffmpeg合并B站视频及格式转换的实例代码

    2021-06-09 21:14:00
  • Python面向对象总结及类与正则表达式详解

    2021-08-12 13:23:34
  • Windows10下 python3.7 安装 facenet的教程

    2022-06-21 11:17:01
  • 利用Python的装饰器解决Bottle框架中用户验证问题

    2022-10-07 07:49:15
  • python信号量,条件变量和事件详解

    2021-10-06 18:14:23
  • python中xlutils库用法浅析

    2023-06-05 15:22:26
  • Python计算程序运行时间的方法

    2023-08-09 09:53:50
  • 解决Oracle安装遇到Enterprise Manager配置失败问题

    2024-01-21 03:48:28
  • Perl哈希表用法解析

    2023-08-23 19:12:48
  • python实现多线程的两种方式

    2022-09-30 00:54:15
  • python批量修改文件名的实现代码

    2023-08-02 19:06:53
  • asp之家 网络编程 m.aspxhome.com