利用Go plugin实现动态加载库文件

  • 调用文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package main

import (
"fmt"
"os"
"plugin"
"strings"
"sync"
"time"
)

var (
fn func() // plugin 动态加载的函数
fnLock sync.RWMutex
)

func main() {

tick := time.NewTicker(time.Second)
inputCh := readInput()

for {
select {
case <-tick.C:
execFn()
case file := <-inputCh:
fmt.Printf("loading plugin file : %s\n", file)
load(file)
}
}
}

func readInput() <-chan string {
ch := make(chan string, 1)
go func() {
b := make([]byte, 16)
for {
n, err := os.Stdin.Read(b)
if err != nil {
//
}
if n > 0 {
ch <- string(b[:n])
}
}
}()
return ch
}

func load(file string) {
file = strings.TrimSpace(file)
p, err := plugin.Open("./myplugin/" + file)
if err != nil {
fmt.Printf("Open plugin error: %v\n", err)
return
}
sb, err := p.Lookup("Print")
if err != nil {
fmt.Printf("plugin lookup failed, error: %v\n", err)
return
}
nfn, _ := sb.(func())

fnLock.Lock()
fn = nfn
fnLock.Unlock()
}

func execFn() {
if fn != nil {
fnLock.RLock()
defer fnLock.RUnlock()
fn()
}
}
  • 上面的代码模拟了一个简单的服务场景。
    1. 监听用户输入,模拟库文件的更新(库文件不能同名,否则会当作同一个文件)
    2. 在库文件加载进来后实现库函数的调用
  • 库文件
1
2
3
4
5
6
7
8
package main

import "fmt"

func Print() {
fmt.Println("这是p1加载的plugin“)
// fmt.Println("这是p2加载的plugin")
}
  • 编译成库文件

    1
    2
    go build -buildmode=plugin -o print.so p1.go
    # go build -buildmode=plugin -o print2.so p2.go
  • 终端输入编译后的文件路径,获取终端的输出

upload successful