golang_plugin

golang plugin的依赖问题

此文中涉及的plugin运行环境为mac 10.14,go版本为1.11
主要是想讨论一下插件依赖的第三方库的问题.
例子是在https://github.com/vladimirvivien/go-plugin-example一文基础之上.

简单插件

1.主程序

package main

import (
	"fmt"
	"os"
	"plugin"
)

type Greeter interface {
	Greet()
}

func main() {
	// load module
	// 1. open the so file to load the symbols
	plug, err := plugin.Open("./eng/eng.so")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// 2. look up a symbol (an exported function or variable)
	// in this case, variable Greeter
	symGreeter, err := plug.Lookup("Greeter")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// 3. Assert that loaded symbol is of a desired type
	// in this case interface type Greeter (defined above)
	var greeter Greeter
	greeter, ok := symGreeter.(Greeter)
	if !ok {
		fmt.Println("unexpected type from module symbol")
		os.Exit(1)
	}

	// 4. use the module
	greeter.Greet()

}

2. plugin代码

package main

import "fmt"

type greeting string

func (g greeting) Greet() {
	fmt.Println("Hello Universe")
}

// exported
var Greeter greeting

3. plugin编译方法

go build -buildmode=plugin -o eng/eng.so eng/greeter.go

4. 运行结果

go run main.go
Hello Universe

插件与主程序依赖第三方库的问题

如果主程序和插件都依赖第三方库会有什么问题呢?他们是共享一份代码?还是完全独立的copy呢?
这就类似于c语言动态链接库的依赖,但是应该又不一样. 以实验结果说话吧.

1. 同时依赖的第三方库

package anotherlib
var ShareVariable =7

2. 运行结果

和平时常见的动态库行为一致,也就是说主程序和插件共享了一份运行代码,也共享了一份运行变量.

引入了vendor的问题

实际项目中,可能代码都会使用vendor来管理自己的第三方依赖库.
这时候就会出现不一致的情况.也就是说因为主程序使用了vendor或者插件使用了vendor,
那么这时候go runtime就会认为插件和主程序用的不是同一个第三方依赖库,这时候就会出现和预期不一致的情况.

完整的代码

我已经把代码放在github,刚兴趣可以下载运行,

main.go

package main

import (
	"fmt"
	"os"
	"plugin"
	"github.com/nkbai/blog/goplugin/anotherlib"
)

type Greeter interface {
	Greet()
	GetShareVariable() int
}

func main() {
	// load module
	// 1. open the so file to load the symbols
	plug, err := plugin.Open("./eng/eng.so")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// 2. look up a symbol (an exported function or variable)
	// in this case, variable Greeter
	symGreeter, err := plug.Lookup("Greeter")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// 3. Assert that loaded symbol is of a desired type
	// in this case interface type Greeter (defined above)
	var greeter Greeter
	greeter, ok := symGreeter.(Greeter)
	if !ok {
		fmt.Println("unexpected type from module symbol")
		os.Exit(1)
	}

	// 4. use the module
	greeter.Greet()

	fmt.Println("anotherlib in main")
	fmt.Println(anotherlib.ShareVariable)
	fmt.Printf("plugin anotherlib =%d\n",greeter.GetShareVariable())
	fmt.Println("change anotherlib's variable")
	anotherlib.ShareVariable=5
	fmt.Printf("main share=%d,plugin share=%d\n",anotherlib.ShareVariable,greeter.GetShareVariable())
	//可以看到输出都是5

	//下面这种情况将会出现不一致的情况
	testpluginvendor()
}

func testpluginvendor(){
		// load module
	// 1. open the so file to load the symbols
	plug, err := plugin.Open("pluginwithvendor/eng.so")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// 2. look up a symbol (an exported function or variable)
	// in this case, variable Greeter
	symGreeter, err := plug.Lookup("Greeter")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// 3. Assert that loaded symbol is of a desired type
	// in this case interface type Greeter (defined above)
	var greeter Greeter
	greeter, ok := symGreeter.(Greeter)
	if !ok {
		fmt.Println("unexpected type from module symbol")
		os.Exit(1)
	}

	// 4. use the module
	greeter.Greet()
	fmt.Println("call plugin withvendor")
	fmt.Println("anotherlib in main")
	fmt.Println(anotherlib.ShareVariable)
	fmt.Printf("plugin anotherlib =%d\n",greeter.GetShareVariable())
	fmt.Println("change anotherlib's variable")
	anotherlib.ShareVariable=5
	fmt.Printf("main share=%d,plugin share=%d\n",anotherlib.ShareVariable,greeter.GetShareVariable())
	//可以看到输出并不一致
}

plugin eng.go

package main

import "fmt"
import "github.com/nkbai/blog/goplugin/anotherlib"

type greeting string

func (g greeting) Greet() {
	fmt.Println("Hello Universe")
}
func (g greeting) GetShareVariable() int{
	return anotherlib.ShareVariable
}
// exported
var Greeter greeting

第三方依赖库 anotherlib.go

package anotherlib
var ShareVariable =7