【Go】记Go使用plugin插件功能出现plugin.Open: plugin was built with a different version of package 解决方法
背景
项目中由于需要底层业务需要动态调整,调研后组里决定使用plugin方法来解决动态加载业务的问题。 在使用过程中发现,当公共库更新后,只要平台程序或者插件并不能同时更新,程序在动态加载插件时候会出现
plugin.Open: plugin was built with a different version of package`
导致插件加载不成功。
环境: Go Version:1.9.2 GOOS=“linux” GOARCH=“amd64” GCCGO=“gccgo” CC=“gcc” CGO_ENABLED=“1”
正常运行的火焰图:
平台实例代码:
p, err := plugin.Open(pluginPath)
if err != nil {
log.Logger.Warnf("[插件更新] 打开插件[%s]失败,详情:[%v]", pluginPath, err)
return
}
f, err := p.Lookup("Version")
if err != nil {
log.Logger.Warnf("[插件更新] 获取插件[%s]版本失败,详情:[%v]", pluginPath, err)
return
}
version := f.(func() string)()
log.Logger.Infof("[插件更新] 载入插件[%s]成功,BU名称:[%s],版本:[%s]", pluginPath, refer, version)
业务插件实例代码:
package main
const(
VERSION = "XX_XX_XX"
)
//版本号输出
func Version() string {
return VERSION
}
//TODO
原因
通过查看plugin包源代码,可以查到问题出在
$GOROOT/src/plugin/plugin_dlopen.go:116
pluginpath, syms, mismatchpkg := lastmoduleinit()
if mismatchpkg != "" {
pluginsMu.Unlock()
return nil, errors.New("plugin.Open: plugin was built with a different version of package " + mismatchpkg)
}
核心的问题是在于mismatchpkg
即存在有不匹配的依赖链接包。再深入到lastmoduleinit()
函数中查看不匹配的原因是什么
$GOROOT/src/runtime/plugin.go:45
for _, pkghash := range md.pkghashes {
if pkghash.linktimehash != *pkghash.runtimehash {
return "", nil, pkghash.modulename
}
}
真正的原因出在了这里,在载入插件时候,Go内部会获取插件的ABI信息,并保存其依赖项链接信息
与运行环境信息
的哈希。因此当运行程序载入插件时候,会获取相关信息并与程序内部依赖项信息进行比对。
而我们实际情况是,在插件
和平台程序
共同调用某一个包的时候,这个包改变了。但是插件和平台程序并未同时编译更新。因此造成了核验失败。
相关讨论: https://github.com/golang/go/issues/21373
解决方法
目前调研后测试通过的解决方法有以下两种:
1.平台程序与插件同时更新。 > 对于集成项目,插件需要调用到平台package的情况,在平台程序更新的时候,也同时把有调用到新修改package的插件也给更新,并同时部署。
2.独立共用package > 对于业务插件可以独立出来而且与平台耦合度不强的情况,可以将
公用的package独立
出来一份单独提供给插件程序调用,独立维护。平台的package更新将不再影响插件的依赖项。