Golang Go语言新玩具来了 go-hotfix,用于运行时热修复函数
基于 monkey-patch + plugin 机制实现的热修复方案,用于实现不停服修复 bug!!
仓库地址: https://github.com/go-hotfix/hotfix
感兴趣可以玩一下 example/webapp 这个例子,注意仅支持 Linux ( ps: Windows 可以在 wsl2 里面玩)
hotfix
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
Features
- 支持指定包级别/类级别/函数级别热补丁支持
- Supported for hot patching at package/class/function
- 支持导出函数/私有函数/成员方法修补
- Support for exporting functions/private functions/member methods patching
- 基于monkey-patch + plugin机制实现
- Implemented based on monkey-patch + plugin
- 线程安全, 使用
stw
确保所有协程都进入安全点从而实现线程安全的补丁 - Thread safety, use
stw
to ensure that all coroutines enter safe points to hot patching
Limits
- 受限于go plugin仅支持 Linux, FreeBSD, macOS ,其他平台目前不支持
- The go plugin is currently only supported on Linux, FreeBSD, and macOS platforms; other platforms are not supported
- 加载的补丁包无法卸载,如果热修复次数过多可能导致较大的内存占用
- The loaded patch package cannot be uninstalled. Too many hot fixes may result in large memory usage
- 不支持对闭包进行修复,需要热修复的逻辑不要放在闭包中
- Closures are not supported, and logic that requires hotfixes should not be placed in closures
- 不能修改已有数据结构和函数签名,否则可能将导致程序崩溃,应该仅用于 bug 修复
- Cannot modify existing data structures and function signatures, as this may lead to program crashes. It should only be used for bug fixes
- 编译时请保留调试符号,并且禁用函数内联
-gcflags=all=-l
- Please keep the debugging symbols when compiling, and disable function inline
-gcflags=all=-l
- 编译错误
invalid reference to xxxx
是因为go1.23
开始限制了go:linkname
功能,必须添加编译参数关闭限制-ldflags=-checklinkname=0
- The compilation error
invalid reference to xxxx
is becausego1.23
began to limit thego:linkname
function, and the compilation parameter must be added to turn off the restriction-ldflags=-checklinkname=0
- 补丁包的的编译环境必须和主程序一致,包括 go 编译器版本,编译参数,依赖等,否则加载补丁包将会失败
- The patch package's build environment must match that of the main program, including the Go compiler version, compilation parameters, dependencies, etc., otherwise loading the patch package will fail
- 补丁包的
main
包下面的init
会首先调用一次,请注意不要重复初始化 - The 'init' under the 'main' package of the patch package will be called once first, be careful not to initialize it repeatedly
- 打补丁包时
main
包必须产生变化,否则可能出现plugin already loaded
错误,推荐使用-ldflags="-X main.HotfixVersion=v1.0.1"
指定版本号, 确保每次编译补丁包都会有变化 - The
main
package must be changed when applying the patch package, otherwise theplugin 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 - 该方案处于实验性质,尚未经过严格验证
- The solution is experimental in nature and has not yet been rigorously validated
Example
Golang Go语言新玩具来了 go-hotfix,用于运行时热修复函数
看着不错,学习下
更多关于Golang Go语言新玩具来了 go-hotfix,用于运行时热修复函数的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
用 plugin 标准库的话项目得编译成动态链接的 ELF ,就这一点已经很难受了
不用拆分代码,原来编译成 exe 的项目,只需要额外加一个编译成 plugin 就行,可以参考 demo ,不需要做任何代码的拆分,直接编译 plugin 就行
很好,我用 k8s 滚动发布
#3 没懂我的意思。用 Go 的项目一般都是静态链接,一个 ELF 扔上去完事儿。你这个工具用到了 plugin 标准库,用这个工具的话项目必须得动态链接。你可以把 example 编译成静态链接的 ELF ,然后再试试能不能 hotfix 。
按照之前玩儿 go plugin 的经验,这东西还是有点难用的,加载之后不能卸载 + 出问题不好排查。而且还有一个问题就是,monkey patch 这东西原作者 license 里明确说了不授权任何人使用,这么搞没版权问题么。
点了下帖子里的 monkey patch 链接是跳到了 gohook ,看来不是用的之前我看到过的那个 monkey patch 的库。
目前的默认实现是 gohook 也支持使用自定义的 patch 库
参考这个脚本 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 .
不知是否是你说的这种情况
确实如此,缺点和限制都很多,除了必要情况下一般也不会去使用它
k8s 不错,特别是无状态应用几乎可以做到无感更新,但是对于一个长期运行的有状态服务器,比如游戏服务器,k8s 通常不是首选运行环境,业界通常利用 ab/滚动更新的方式来实现更新,但是这种方式更新非常的慢,通常需要以小时为单位计,如果有刷装备/金币的 bug 等到几个小时恐怕全服玩家都刷个遍了 🤣
#9 你确定这样编译出来的主程序是静态链接的?用 file 命令确认下吧。如果真是静态链接的,调 plugin.Open()
的时候会报 plugin: not implemented
错误的。
你的意思是关闭 cgo ,那肯定的都用 plugin 了 cgo 是需要启用的不然肯定无法加载插件了,这个方案也是要求启用 cgo ,因为本质上 plugin open 就是调用 dlopen 加载的
关于您提到的“go-hotfix”这一用于Go语言运行时热修复函数的工具,确实是一个值得关注的创新。在快速迭代的软件开发环境中,能够在不重启服务的情况下修复线上bug,对于提升系统的稳定性和维护效率具有重要意义。
go-hotfix通过动态替换运行时函数的方式实现了热修复功能。它允许开发者在运行时动态地加载并应用新的函数实现,从而替代原有的存在缺陷的函数。这种技术不仅能够显著减少因修复bug而带来的服务中断时间,还能够提高系统的可用性和用户体验。
然而,值得注意的是,使用go-hotfix也带来了一定的技术挑战和风险。首先,热修复需要在不破坏现有程序状态的前提下进行,这就要求开发者对程序的内存布局和运行时有深入的理解。其次,由于go-hotfix直接操作运行时内存,因此可能会引入新的稳定性和安全性问题。
因此,在使用go-hotfix时,建议开发者充分测试其功能,并谨慎评估其可能带来的风险。同时,也需要注意go-hotfix与Go语言社区和生态的兼容性,以确保其能够长期稳定地为Go语言开发者提供热修复支持。
总的来说,go-hotfix为Go语言开发者提供了一种强大的运行时热修复工具,但使用时需要权衡其带来的好处和风险。希望这一工具能够为您的项目带来便利,并推动Go语言技术的不断发展。