基于 monkey-patch + plugin 机制实现的热修复方案,用于实现不停服修复 bug!!
仓库地址: https://github.com/go-hotfix/hotfix
感兴趣可以玩一下 example/webapp 这个例子,注意仅支持 Linux ( ps: Windows 可以在 wsl2 里面玩)
hotfix is a golang function hot-fix solution
警告: 目前尚未经过严格测试,请勿用于生产环境
Warning: This has not been rigorously tested, do not use in production environment
注意: 不支持 Windows
Note: Windows is not supported
注意: 为了确保函数都能被修补,需要关闭函数内联,会因此损失一些性能
Note: To ensure that all functions can be patched, function inlining needs to be disabled, which will result in a loss of some performance
stw
确保所有协程都进入安全点从而实现线程安全的补丁stw
to ensure that all coroutines enter safe points to hot patching-gcflags=all=-l
-gcflags=all=-l
invalid reference to xxxx
是因为 go1.23
开始限制了go:linkname
功能,必须添加编译参数关闭限制-ldflags=-checklinkname=0
invalid reference to xxxx
is because go1.23
began to limit the go:linkname
function, and the compilation parameter must be added to turn off the restriction -ldflags=-checklinkname=0
main
包下面的init
会首先调用一次,请注意不要重复初始化main
包必须产生变化,否则可能出现 plugin already loaded
错误,推荐使用 -ldflags="-X main.HotfixVersion=v1.0.1"
指定版本号, 确保每次编译补丁包都会有变化main
package must be changed when applying the patch package, otherwise the plugin already loaded
error may occur. It is recommended to use -ldflags="-X main.HotfixVersion=v1.0.1"
to specify the version number to ensure that the patch package is compiled every time There will be changes 1
wswch4444 79 天前
看着不错,学习下
|
2
Nitroethane 79 天前
用 plugin 标准库的话项目得编译成动态链接的 ELF ,就这一点已经很难受了
|
3
limpo OP @Nitroethane 不用拆分代码,原来编译成 exe 的项目,只需要额外加一个编译成 plugin 就行,可以参考 demo ,不需要做任何代码的拆分,直接编译 plugin 就行
|
4
mshadow 79 天前 via Android
很好,我用 k8s 滚动发布
|
5
Nitroethane 79 天前
@limpo #3 没懂我的意思。用 Go 的项目一般都是静态链接,一个 ELF 扔上去完事儿。你这个工具用到了 plugin 标准库,用这个工具的话项目必须得动态链接。你可以把 example 编译成静态链接的 ELF ,然后再试试能不能 hotfix 。
|
6
azraeljack 79 天前
按照之前玩儿 go plugin 的经验,这东西还是有点难用的,加载之后不能卸载 + 出问题不好排查。而且还有一个问题就是,monkey patch 这东西原作者 license 里明确说了不授权任何人使用,这么搞没版权问题么。
|
7
azraeljack 79 天前
点了下帖子里的 monkey patch 链接是跳到了 gohook ,看来不是用的之前我看到过的那个 monkey patch 的库。
|
8
limpo OP @azraeljack 目前的默认实现是 gohook 也支持使用自定义的 patch 库
|
9
limpo OP @Nitroethane 参考这个脚本 https://github.com/go-hotfix/hotfix/blob/main/example/webapp/run_webapp.sh
# 这里静态编译主程序 echo "build webapp..." go build -gcflags=all=-l -ldflags="-X main.HotfixVersion=main" -o webapp . echo "please modify v1 plugin, press enter key to continue..." read input # 这里是编译为 so 动态库( plugin ) echo "build webapp plugin v1..." go build -gcflags=all=-l -buildmode=plugin -ldflags="-X main.HotfixVersion=v1" -o webapp_v1.so . 不知是否是你说的这种情况 |
10
limpo OP @azraeljack 确实如此,缺点和限制都很多,除了必要情况下一般也不会去使用它
|
11
limpo OP @mshadow k8s 不错,特别是无状态应用几乎可以做到无感更新,但是对于一个长期运行的有状态服务器,比如游戏服务器,k8s 通常不是首选运行环境,业界通常利用 ab/滚动更新的方式来实现更新,但是这种方式更新非常的慢,通常需要以小时为单位计,如果有刷装备/金币的 bug 等到几个小时恐怕全服玩家都刷个遍了 🤣
|
12
Nitroethane 79 天前
@limpo #9 你确定这样编译出来的主程序是静态链接的?用 file 命令确认下吧。如果真是静态链接的,调 `plugin.Open()` 的时候会报 `plugin: not implemented` 错误的。
|
13
limpo OP @Nitroethane 你的意思是关闭 cgo ,那肯定的都用 plugin 了 cgo 是需要启用的不然肯定无法加载插件了,这个方案也是要求启用 cgo ,因为本质上 plugin open 就是调用 dlopen 加载的
|