
Hotswap 为 go 语言提供了一套相当完整的代码热更解决方案,热更过程不会中断或阻塞任何执行中的函数,更不会重启服务器。此方案建立在 go 语言的 plugin 机制之上。
Plugin.InvokeFunc() 从宿主调用插件中的函数PluginManager.Vault.Extension 和 PluginManager.Vault.DataBag 向宿主暴露插件中的数据和函数live function、live type 和 live data 用最新代码执行异步任务Export() 向其它插件暴露函数Import() 声明、建立对其它插件的依赖go install github.com/edwingeng/hotswap/cli/hotswap
Usage:
hotswap build [flags] <pluginDir> <outputDir> -- [buildFlags]
Examples:
hotswap build plugin/foo bin
hotswap build -v plugin/foo bin -- -race
hotswap build --staticLinking plugin/foo plugin
Flags:
--debug enable debug mode
--exclude string go-regexp matching files to exclude from included
--goBuild if --goBuild=false, skip the go build procedure (default true)
-h, --help help for build
--include string go-regexp matching files to include in addition to .go files
--leaveTemps do not delete temporary files
--livePrefix string case-insensitive name prefix of live functions and live types (default "live_")
--staticLinking generate code for static linking instead of building a plugin
-v, --verbose enable verbose mode
你可以在 demo 目录下找到这些例子。为了更直观的体验,运行 run.sh 启动服务器,再运行 reload.sh 热更插件。
hello 展示了这套方案的基本用法, 包括怎样组织宿主和插件、怎样编译宿主和插件、怎样在服务器启动时加载插件、怎样使用 InvokeEach、以及怎样热更。extension 是个关于自定义扩展的例子,它可以告诉你 PluginManager.Vault.Extension 的用法。小提示: WithExtensionNewer()。livex 比较复杂. 它展示了 live function, live type 和 live data 的用法。slink 展示了静态链接的使用方法。在 MacOS 和 Windows 下,用静态链接才能上调试器(delve)调试。trine 是最后一个例子,它展示了插件的依赖机制。每个插件都要在其根 package 下定义以下函数:
// OnLoad gets called after all plugins are successfully loaded and before the Vault is initialized.
func OnLoad(data interface{}) error {
return nil
}
// OnInit gets called after the execution of all OnLoad functions. The Vault is ready now.
func OnInit(sharedVault *vault.Vault) error {
return nil
}
// OnFree gets called at some time after a reload.
func OnFree() {
}
// Export returns an object to export to other plugins.
func Export() interface{} {
return nil
}
// Import returns an object indicating the dependencies of the plugin.
func Import() interface{} {
return nil
}
// InvokeFunc invokes the specified function.
func InvokeFunc(name string, params ...interface{}) (interface{}, error) {
return nil, nil
}
// Reloadable indicates whether the plugin is reloadable.
func Reloadable() bool {
return true
}
1. Reloadable
2. Export
3. Import
4. OnLoad
5. Vault Initialization
6. OnInit
CGO_ENABLED=1,并指定编译参数 -trimpath。go 认为不同插件版本中的同一类型是不同类型,跨版本赋值、拆箱是行不通的。你可以借助 live function, live type 和 live data 规避这一陷阱。plugin 的限制导致的。不过你有个清理缓存的机会:OnFree。git 和 go module 管理代码。live function 是以 live_ 为名字前缀(大小写不敏感)的函数,所有这类函数都会被自动收集起来并存入 PluginManager.Vault.LiveFuncs。例如:func live_Foo(jobData live.Data) error {
return nil
}
live type 是以 live_ 为名字前缀(大小写不敏感)的(struct)类型,所有这类 struct 都会被自动收集起来并存入 PluginManager.Vault.LiveTypes。例如:type Live_Bar struct {
N int
}
live data 是个类型隔离器。你可以在创建异步任务时把任务数据转成 live data 对象,再在执行任务时把数据恢复回来。livex 包含更多细节。$ claude mcp add hotswap \
-- python -m otcore.mcp_server <graph>