Go语言plugin
Golang是静态编译型语言,在编译时就将所有引用的库全部加载打包到最终的可执行程序中(CGO除外),在运行时不需要动态加载其他共享库。这样设计的好处很多,但是如果某些场景下需要实现功能的可插拔则非常不方便。 在Go 1.8更新中,Go语言提供了Go Plugin机制,可以在运行时动态加载外部功能。
Go的 Plugin 实现
Go语言提供了标准库 plugin
来实现插件机制。其接口非常简单
type Plugin
func Open(path string) (*Plugin, error)
func (p *Plugin) Lookup(symName string) (Symbol, error)
type Symbol
- Open : 用于加载编译好的插件。
- Lookup : 搜索symbol。
- Symbol : 是指向变量或函数的指针。
示例
创建目录结构
mkdir hello-plugin world-plugin testing
cd hello-plugin
go mod init app/hello-plugin
cd ../world-plugin
go mod init app/world-plugin
cd ../testing
go mod init app/testing
touch ./{hello-plugin,world-plugin,testing}/mian.go
目录结构如下:
.
├── hello-plugin
│ ├── go.mod
│ └── mian.go
├── testing
│ ├── go.mod
│ └── mian.go
└── world-plugin
├── go.mod
└── mian.go
hello-plugin/mian.go
package main
import "fmt"
func Say() {
fmt.Println("Hello")
}
world-plugin/mian.go
package main
import "fmt"
func Say() {
fmt.Println("World")
}
testing/main.go
package main
import (
"log"
"plugin"
)
func main() {
p, err := plugin.Open("./say.so")
if err != nil {
log.Fatalf("error open plugin: %v", err)
}
s, err := p.Lookup("Say")
if err != nil {
log.Fatalf("error lookup component: %v", err)
}
if say, ok := s.(func()); ok {
say()
}
}
编译与构建
cd hello-plugin
go build --buildmode=plugin -o say.so
cd ../world-plugin
go build --buildmode=plugin -o say.so
cd ../testing
go build
测试
cp ../hello-plugin/say.so .
./testing
rm say.so
cp ../world-plugin/say.so .
./testing
总结
在Go语言中使用plugin的基本流程如下
- 编写插件
- 编译为so
- 在主程序中加载插件
- 搜索可用组件(变量或者函数)
- 对组件执行类型断言
- 调用组件