Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

go-plugin

概述

在 1.8 版本中开放插件(Plugin)的支持,这意味着现在能从 Go 中动态加载部分函数。

在 Go 1.8(不是 1.18)版本中,Go 引入了 plugin 包,它允许 Go 程序通过动态加载共享库(.so 文件)来扩展功能。这对于需要插件化、动态加载功能模块的场景非常有用。

plugin 基本概念:

  • 共享库:是一个编译后的 Go 代码的动态库(.so 文件),可以在运行时被加载。
  • 符号(Symbol):共享库中的变量和函数在加载时可以被查找并调用。

示例:如何使用 plugin

1. 创建插件(共享库)

首先,我们创建一个简单的 Go 文件 plugin.go,这个文件将被编译成共享库。

1
2
3
4
5
6
7
8
9
10
11
12
// plugin.go
package main

import "fmt"

// 声明一个函数,这个函数将在插件中被导出
func Hello() {
fmt.Println("Hello from the plugin!")
}

// 声明一个导出的变量
var Name string = "PluginName"

编译这个 Go 文件为共享库:

1
go build -buildmode=plugin -o myplugin.so plugin.go

2. 在主程序中使用插件

接下来,我们编写主程序来加载并使用这个插件。

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
// main.go
package main

import (
"fmt"
"plugin"
)

func main() {
// 打开插件
p, err := plugin.Open("myplugin.so")
if err != nil {
fmt.Println("Failed to load plugin:", err)
return
}

// 查找并使用 Hello 函数
hello, err := p.Lookup("Hello")
if err != nil {
fmt.Println("Failed to find Hello:", err)
return
}

// 通过类型断言转换为函数并调用
hello.(func())()

// 查找并使用 Name 变量
name, err := p.Lookup("Name")
if err != nil {
fmt.Println("Failed to find Name:", err)
return
}

// 通过类型断言转换为字符串指针并输出
fmt.Println("Plugin name:", *name.(*string))
}

编译并运行主程序:

1
go run main.go

3. 运行结果

1
2
Hello from the plugin!
Plugin name: PluginName

总结

  • plugin 允许在 Go 程序中动态加载和使用编译好的 Go 代码(共享库)。
  • 可以通过 plugin.Open() 打开 .so 文件,然后通过 Lookup() 查找插件中的函数和变量,并使用类型断言来调用这些符号。

这种机制非常适合需要在运行时扩展功能的应用程序,比如可插拔的应用架构、动态扩展的系统等。