Q1:说明 go plugin 使用场景,并编写一个使用 plugin 的 demo

Go plugin支持将Go包编译为共享库(.so)的形式单独发布,主程序可以在运行时动态加载这些编译为动态共享库文件的go plugin,从中提取导出(exported)变量或函数的符号并在主程序的包中使用。Go plugin的这种特性为Go开发人员提供更多的灵活性,我们可以用之实现支持热插拔的插件系统。

demo 通过不同插件实现io.StringWriter,可将输出保存到不同地方

plugin/plugin.go

package main

import (
	"fmt"
	"log"
	"os"
)

func init() {
	log.Println("stdoutStringWriter init")
}

type stdoutStringWriter struct{}

func (w *stdoutStringWriter) WriteString(s string) (n int, err error) {
	return fmt.Fprint(os.Stdout, s)
}

var StringWriter stdoutStringWriter

main.go

package main

import (
	"io"
	"log"
	"plugin"
)

func main() {
	log.SetFlags(log.Lshortfile | log.LstdFlags)
	p, err := plugin.Open("./plugin/plugin.so")
	if err != nil {
		panic(err)
	}
	w, err := p.Lookup("StringWriter")
	if err != nil {
		panic(err)
	}
	sw, ok := w.(io.StringWriter)
	if !ok {
		panic(err)
	}
	n, err := sw.WriteString("Hello World!\n")
	if err != nil {
		log.Println(err)
	}
	log.Println("written:", n)
}

stdout

$ go build -buildmode=plugin -o plugin/plugin.so plugin/plugin.go
$ go run .
2024/05/29 14:37:02 plugin.go:10: stdoutStringWriter init
Hello World!
2024/05/29 14:37:02 main.go:27: written: 13

plugin.go 的 log.Println 也受到了 main.go 的 log.SetFlags 影响

Q2:说明 go plugin 使用约束

  • go plugin只支持Linux, FreeBSD和macOS
  • plugin包名必须为main
  • 目前只能加载,不能卸载so
  • 主程序与plugin的共同(间接)依赖包的版本必须一致
  • 如果采用mod=vendor构建,那么主程序和plugin必须基于同一个vendor目录构建
  • 主程序与plugin使用的编译器版本必须一致
  • 使用plugin的主程序仅能使用动态链接

参考