Golang实践:so插件plugin
go Plugin介绍
Package plugin implements loading and symbol resolution of Go plugins.
A plugin is a Go main package with exported functions and variables that has been built with:
go build -buildmode=plugin When a plugin is first opened, the init functions of all packages not already part of the program are called. The main function is not run. A plugin is only initialized once, and cannot be closed.
Currently plugins are only supported on Linux, FreeBSD, and macOS. Please report any issues.
main函数执行前plugin的init函数不会执行,插件只被初始化一次,不能被关闭
只支持Linux, FreeBSD, and macOS,不支持Windows
plugin库接口文档
https://golang.google.cn/pkg/plugin/#pkg-overview
使用场景
-
需要加密的核心算法,核心业务逻辑可以可以编译成plugin插件
-
对于不同功能加载相应的模块并调用相关的模块
-
编译出的文件给不同的编程语言用(C/java/python/等)
注意
Go plugin判断两个插件是否相同是通过比较pluginpath实现的,如果没有指定pluginpath,则由内部的算法生成, 生成的格式为plugin/unnamed-“ + root.Package.Internal.BuildID 。 这种情况下,如果两个插件的文件名不同,引用包不同,或者引用的cgo不同,则会生成不同的插件,同时加载不会有问题。 但是如果两个插件的文件名相同,相关的引用包也相同,则可能生成相同的插件,即使插件内包含的方法和变量不同,实现也不同。 判断插件相同,热加载不会成功,也就意味着老插件不支持覆盖更新。
最好在编译的指定pluginpath,同时方便版本跟踪。 目前生产环境建议一些公共库无服务依赖的函数,例如算法库之类的。
实战
plugin1.go
package main
import (
"fmt"
"log"
)
func init() {
log.Println("plugin1 init ....")
}
type Plugin1 struct {
Name string
}
func (p Plugin1) DoServiceTask() error {
log.Println(fmt.Sprintf("Plugin %s working ...", p.Name))
return nil
}
//go build -buildmode=plugin -o=plugin1.so plugin1.go
// PluginService
var PluginService = Plugin1{"Service"}
plugin2.go
package main
import (
"fmt"
"log"
)
func init() {
log.Println("plugin2 init ....")
}
type Plugin2 struct {
Name string
}
func (p Plugin2) DoLabelTask() error {
log.Println(fmt.Sprintf("Plugin %s working ...", p.Name))
return nil
}
//go build -buildmode=plugin -o=plugin1.so plugin1.go
// exported as symbol named "PluginLabel"
var PluginLabel = Plugin2{"label"}
plugin3.go
package main
import (
"fmt"
"log"
)
func init() {
log.Println("plugin3 init ....")
}
type Plugin3 struct {
Name string
}
func (p Plugin3) DoRiskPortTask() error {
log.Println(fmt.Sprintf("Plugin %s working ...", p.Name))
return nil
}
//go build -buildmode=plugin -o=plugin1.so plugin1.go
// PluginRiskPort exported as symbol named "PluginRiskPort"
var PluginRiskPort = Plugin3{"riskport"}
use_plugin.go
package main
import (
"fmt"
"log"
"plugin"
)
type PluginInf interface {
DoServiceTask() error
}
type PluginInf2 interface {
DoLabelTask() error
}
type PluginInf3 interface {
DoRiskPortTask() error
}
func UsePlugin1() error {
plug, err := plugin.Open("./plugin1.so")
if err != nil {
log.Println("error", err.Error())
return err
}
log.Println("plugin opened")
s, err := plug.Lookup("PluginService")
if err != nil {
log.Println(err.Error())
return err
}
service, ok := s.(PluginInf)
if !ok {
msg := "unexpected type from module symbol"
log.Println(msg)
return fmt.Errorf("error: %s", msg)
}
if err := service.DoServiceTask(); err != nil {
log.Println("use plugin service failed, ", err)
return err
}
return nil
}
func UsePlugin2() error {
plug, err := plugin.Open("./plugin2.so")
if err != nil {
log.Println("error", err.Error())
return err
}
log.Println("plugin opened")
s, err := plug.Lookup("PluginLabel")
if err != nil {
log.Println(err.Error())
return err
}
service, ok := s.(PluginInf2)
if !ok {
msg := "unexpected type from module symbol"
log.Println(msg)
return fmt.Errorf("error: %s", msg)
}
if err := service.DoLabelTask(); err != nil {
log.Println("use plugin label failed, ", err)
return err
}
return nil
}
func UsePlugin3() error {
plug, err := plugin.Open("./plugin3.so")
if err != nil {
log.Println("error", err.Error())
return err
}
log.Println("plugin opened")
s, err := plug.Lookup("PluginRiskPort")
if err != nil {
log.Println(err.Error())
return err
}
service, ok := s.(PluginInf3)
if !ok {
msg := "unexpected type from module symbol"
log.Println(msg)
return fmt.Errorf("error: %s", msg)
}
if err := service.DoRiskPortTask(); err != nil {
log.Println("use plugin3 failed, ", err)
return err
}
return nil
}
server.go
package main
import (
"fmt"
"net/http"
"time"
)
func ServerHandle1(w http.ResponseWriter, r *http.Request) {
UsePlugin1()
w.Write([]byte(fmt.Sprintf("use plugin1, time now: %+v\n", time.Now())))
}
func ServerHandle2(w http.ResponseWriter, r *http.Request) {
UsePlugin2()
w.Write([]byte(fmt.Sprintf("use plugin2, time now: %+v\n", time.Now())))
}
func ServerHandle3(w http.ResponseWriter, r *http.Request) {
UsePlugin3()
w.Write([]byte(fmt.Sprintf("use plugin3, time now: %+v\n", time.Now())))
}
func StartServer() {
mux := http.NewServeMux()
mux.HandleFunc("/api/v1/mss/service", ServerHandle1)
mux.HandleFunc("/api/v1/mss/label", ServerHandle2)
mux.HandleFunc("/api/v1/mss/port", ServerHandle3)
fmt.Println("Start server....")
http.ListenAndServe("", mux)
}
编译
go build main.go use_plugin.go plugin1.go plugin2.go plugin3.go server.go