前言:
最近在学习fabric 2.1 版本源码,其中在endorserSupportImpl 结构中使用了Go的plugin 技术。所以这里准备记录下Go Plugin 是什么,怎么用。这里部分参考了https://www.jianshu.com/p/4ab799081a99 的介绍(特记录并表示感谢)
Golang是静态编译型语言,在编译时就将所有引用的包(库)全部加载打包到最终的可执行程序(或库文件)中,因此并不能在运行时动态加载其他共享库。Go Plugin提供了这样一种方式,能够让你在运行时动态加载外部功能。
其实应该问为什么要用Plugin,我觉得原因有很多,比如:
可插拔:有了Plugin,我的程序可以根据需要随时替换其中某些部件而不用修改我的程序;动态加载的需要:有些模块只有在运行时才能确定,需要动态加载外部的功能模块;独立开发:Plugin 可以和主程序独立建设,主程序只需要制定好框架,实现默认(模版)功能。Plugin 可根据用户需求随时自行扩展开发,运行时随意替换,提高了程序的可定制性;Golang 对 Plugin 的实现在标准库plugin中。整个接口可以说相当简洁了。
type Plugin struct{ ... } func Open(path string) (*Plugin, error) func (p *Plugin) Lookup(symName string) (Symbol, error) type Symbol interface{}我们可以发现只有两个方法还是很简单的。
type Plugin即Golang加载的插件,与之有关的两个方法:
Open: 根据参数path提供的插件路径加载这个插件,并返回插件这个插件结构的指针*GluginLookup: *Plugin的惟一方法,通过名称symName在插件中寻找对应的变量或方法,以Symbol的形式返回(由于Symbol 是interface 类型的,所以说我们可以从插件中拿到任何类型的可导出元素。)示例:
了解了plugin包的基本功能,按照惯例,我们要用hello world检验下。 准备plugin源码pluginhello.go:
package main import ( "fmt" ) // plugin 源码必须在main 包中 func Hello() { fmt.Println("Hello World From Plugin!") }这里在插件中,定义了一个可导出方法Hello打印Hello World From Plugin!。
编译为可执行程序:
go build --buildmode=plugin -o pluginhello.so pluginhello.go用go build命令,同时制定buildmode为plugin即可。
调用示例:
package main import ( "fmt" "os" "plugin" ) func main() { p, err := plugin.Open("./pluginhello.so") if err != nil { fmt.Println("error open plugin: ", err) os.Exit(-1) } s, err := p.Lookup("Hello") if err != nil { fmt.Println("error lookup Hello: ", err) os.Exit(-1) } if hello, ok := s.(func()); ok { hello() } }首先通过Open方法打开插件,然后通过名称Hello找到插件中的func Hello方法。 注意,由于从插件中找到的任何元素都是以Symbol形式(即interface{})返回,我们需要通过断言的形式对结果进行判断和转换,得到我们需要的类型。 让我们看看效果吧:
go run invokeplugin.go //打印 Hello World From Plugin!成功!!!
