golang版本: 1.5 以上支持动态库,1.8以上支持plugin
go提供的plugin包可以实现热更新的功能。
Go插件是使用-buildmode = plugin
标记编译的一个包,用于生成一个共享对象(.so)库文件。 Go包中的导出的函数和变量被公开为ELF符号,可以使用plugin包在运行时查找并绑定ELF符号。
Go编译器能够使用build flag -buildmode = c-shared
创建C风格的动态共享库。
从1.8版开始,Go插件功能只能在Linux上使用。 很有可能在将来的版本中发生变化。
plugin
Plugin
type Plugin即Golang加载的插件,与之有关的两个方法:
- Open: 根据参数path提供的插件路径加载这个插件,并返回插件这个插件结构的指针*Glugin
- Lookup: *Plugin的惟一方法,通过名称symName在插件中寻找对应的变量或方法,以Symbol的形式返回
Symbol
根据定义type Symbol interface{},Symbol是interface的别名,也就是说,我们可以从插件里面拿到任何类型的可导出元素。
插件实例
实现插件
插件代码的编写和普通的go模块代码一样,主要是package必须是 main。
类似c的头文件,定义一些数据
1 2 3 4 5 6 7 8
| package data
type PluginData struct { Name string Age int Tel string } var DataChan = make(chan PluginData)
|
记得函数名要大写不然无法被外部程序访问(init除外)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package main
import ( "fmt" "data" )
var Value int
var Pd data.PluginData
func init() { fmt.Println("plugin load...") }
func Plugin_fun() { fmt.Printf("Value in plugin: %d\n",Value) }
func ComplexType() { fmt.Println(Pd.Name, Pd.Age, Pd.Tel) Pd.Age = Pd.Age * 4 data.DataChan<-Pd }
|
编译插件
如果要想更好的控制插件的版本,想做更酷的事情,比如:热更新插件。那么可以采用自动注册的方式,新版本的插件加载上来后,自动注册插件版本号,插件平台里优先使用新版本的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #编译go动态库命令,以当前目录名称作为动态库文件名 go build -buildmode=plugin #编译go动态库命令,testPlugin作为动态库文件名 go build -buildmode=plugin testPlugin.go
#编译完后我们可以看到当前目录下有一个testplugin.so文件 #我们也可以通过类似如下命令来生成不同版本的插件 go build -o testplugin_v1.so -buildmode=plugin testPlugin.go
#创建C风格的动态共享库,可供c语言调用 #注意需要在testPlugin中实现main函数,否则无法编译 go build -buildmode=c-shared go build -buildmode=c-shared testPlugin.go
|
使用插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package main
import ( "data" "plugin" )
func main() { so, err := plugin.Open("testPlugin.so") if err != nil { panic(err) } value, err := so.Lookup("Value") if err != nil { panic(err) } plugin_fun, err := so.Lookup("Plugin_fun") if err != nil { panic(err) } *value.(*int) = 7 plugin_fun.(func())()
pd, err := so.Lookup("Pd") if err != nil { panic(err) }
complexType, err := so.Lookup("ComplexType") if err != nil { panic(err) }
*pd.(*data.PluginData) = data.PluginData{ Name: "PluginTest", Age: 11, Tel: "15396656998", }
go complexType.(func())()
select { case m := <-data.DataChan: println(m.Age) } }
|
编译