Package plugin implements loading and symbol resolution of Go plugins.

Golang是静态编译型语言,在编译时就将所有引用的包(库)全部加载打包到最终的可执行程序(或库文件)中, 因此并不能在运行时动态加载其他共享库。而Go Plugin提供了在运行时动态加载外部功能。

plugin应用场景:

  • 可插拔
    • 可以热更新式扩展应用程序的功能列表
    • 应对多变的业务需求,方便功能上下线
    • 对于任意的go应用,能进行增量架构、代码分发以及代码上下线
  • 动态加载的需要
    • 通过plugin可以很方便的对于不同功能加载相应的模块并调用相关的模块
    • 可以针对不同语言(英文、汉语、德语……)加载不同的语言so文件,进行不同的输出
  • 独立开发
    • 可以把编译出的文件给不同的编程语言用(如:c/java/python/lua等)

标准库方法

1
2
3
4
type Plugin struct{ ... }
    func Open(path string) (*Plugin, error)
    func (p *Plugin) Lookup(symName string) (Symbol, error)
type Symbol interface{}
  • Open: 通过参数path路径加载插件,返回*Plugin指针
  • Lookup: *Plugin指针方法,该方法通过名称symName在插件中查找对应的变量或方法,返回Symbol类型
  • Symbol类型是interface{}的别名,也意味着可以从插件里面拿到任何类型的可导出元素

go plugin示例

插件代码,提供一个简单的Add方法

1
2
3
4
5
package main

func Add(x, y int) int {
    return x + y
}

编译插件,生成add.so文件

1
> go build -buildmode=plugin -o add.so add.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
package main

import (
    "fmt"
    "log"
    "net/http"
    "plugin"
    "strconv"
)

func handler(w http.ResponseWriter, r *http.Request) {
    vars := r.URL.Query()
    a := vars.Get("a")
    b := vars.Get("b")
    p, _ := plugin.Open("./add.so")
    add, _ := p.Lookup("Add")
    i, _ := strconv.Atoi(a)
    j, _ := strconv.Atoi(b)
    sum := add.(func(int, int) int)(i, j)
    fmt.Fprintf(w, "%s + %s = %d", a, b, sum)
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

调用http服务

1
2
> go run main.go
> curl -i http://127.0.0.1:8080?a=1&b=1

c-shared示例

示例原文

使用buildmode=plugin会生成.so文件;

使用buildmode=c-shared会生成出来两个文件,一个.so文件,一个.h头文件,和使用c生成的库文件和模块文件一样使用

awesome.go代码

 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
package main
import "C"
import (
    "fmt"
    "math"
    "sort"
    "sync"
)
var count int
var mtx sync.Mutex
//export Add
func Add(a, b int) int {
    return a + b
}
//export Cosine
func Cosine(x float64) float64 {
    return math.Cos(x)
}
//export Sort
func Sort(vals []int) {
    sort.Ints(vals)
}
//export Log
func Log(msg string) int {
    mtx.Lock()
    defer mtx.Unlock()
    fmt.Println(msg)
    count++
    return count
}
func main() {}

编译awesome.go文件,生成awesome.h, awesome.so两个文件

1
> go build -o awesome.so -buildmode=c-shared awesome.go

查看so文件,输出为shared object文件,并exported相关对像的symbols

1
2
3
4
5
6
7
> file awesome.so
awesome.so: Mach-O 64-bit dynamically linked shared library x86_64
> nm awesome.so | grep -e "T _Add" -e "T _Cosine" -e "T _Sort" -e "T _Log"
00000000000a0470 T _Add
00000000000a04d0 T _Cosine
00000000000a0570 T _Log
00000000000a0520 T _Sort

c语言动态链接调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include "awesome.h"
int main() {
    //Call Add() - passing integer params, interger result
    GoInt a = 12;
    GoInt b = 99;
    printf("awesome.Add(12,99) = %d\n", Add(a, b));
    //Call Cosine() - passing float param, float returned
    printf("awesome.Cosine(1) = %f\n", (float)(Cosine(1.0)));
    //Call Sort() - passing an array pointer
    GoInt data[6] = {77, 12, 5, 99, 28, 23};
    GoSlice nums = {data, 6, 6};
    Sort(nums);
    printf("awesome.Sort(77,12,5,99,28,23): ");
    for (int i = 0; i < 6; i++){
        printf("%d,", ((GoInt *)nums.data)[i]);
    }
    printf("\n");
    //Call Log() - passing string value
    GoString msg = {"Hello from C!", 13};
    Log(msg);
}

编译awesome1.c文件并运行

1
2
3
4
5
6
> gcc -o awesome1 awesome1.c ./awesome.so
> ./awesome1
awesome.Add(12,99) = 111
awesome.Cosine(1) = 0.540302
awesome.Sort(77,12,5,99,28,23): 5,12,23,28,77,99,
Hello from C!

c语言动态加载

 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
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
// define types needed
typedef long long go_int;
typedef double go_float64;
typedef struct{void *arr; go_int len; go_int cap;} go_slice;
typedef struct{const char *p; go_int len;} go_str;
int main(int argc, char **argv) {
    void *handle;
    char *error;
    // use dlopen to load shared object
    handle = dlopen ("./awesome.so", RTLD_LAZY);
    if (!handle) {
        fputs (dlerror(), stderr);
        exit(1);
    }
    // resolve Add symbol and assign to fn ptr
    go_int (*add)(go_int, go_int)  = dlsym(handle, "Add");
    if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
    }
    // call Add()
    go_int sum = (*add)(12, 99);
    printf("awesome.Add(12, 99) = %d\n", sum);
    // resolve Cosine symbol
    go_float64 (*cosine)(go_float64) = dlsym(handle, "Cosine");
    if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
    }
    // Call Cosine
    go_float64 cos = (*cosine)(1.0);
    printf("awesome.Cosine(1) = %f\n", cos);
    // resolve Sort symbol
    void (*sort)(go_slice) = dlsym(handle, "Sort");
    if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
    }
    // call Sort
    go_int data[5] = {44,23,7,66,2};
    go_slice nums = {data, 5, 5};
    sort(nums);
    printf("awesome.Sort(44,23,7,66,2): ");
    for (int i = 0; i < 5; i++){
        printf("%d,", ((go_int *)data)[i]);
    }
    printf("\n");
    // resolve Log symbol
    go_int (*log)(go_str) = dlsym(handle, "Log");
    if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
    }
    // call Log
    go_str msg = {"Hello from C!", 13};
    log(msg);
    // close file handle when done
    dlclose(handle);
}

编译awesome2.c文件并运行

1
2
3
4
5
6
> gcc -o awesome2 awesome2.c -ldl
> ./awesome2
awesome.Add(12, 99) = 111
awesome.Cosine(1) = 0.540302
awesome.Sort(44,23,7,66,2): 2,7,23,44,66,
Hello from C!

参考