Go 插件机制
Go 语言的插件机制依赖于动态库(或者叫「共享对象」)实现,让 Go 程序可以在运行时从动态库加载模块,而不是像静态库那样需要静态链接。
注:以 Linux 为例,底层使用了 dlopen / dlsym 等命令。
使用
编译
在 go build
命令中添加 -buildmode=plugin
可以编译出 Go 插件。编译后会得到 .so
文件。
为了让调用方使用,插件代码中可以实现变量、函数和结构体等(需要和调用方约定好协议)。
加载 & 调用
通过 plugin 库可以加载 Go 插件(.so
文件)。
其中:
-
Open
可以指定.so
文件的路径,注册插件并得到 Plugin 结构体(可理解为动态库的句柄)。 -
通过 Plugin 结构体的
Lookup
方法,可以获得动态库句柄中的特定符号Symbol
。 -
Symbol
是一个指针,指向变量或函数。并且,可以转换为变量或函数。
举例
举例说明整体流程:
-
调用方和插件方确定好调用的协议,比如一个 Go 接口。
-
插件方实现一个结构体变量,结构体实现了对应的接口。
-
编译插件,得到
.so
文件。 -
调用方通过
Open
加载插件.so
文件,然后调用Lookup
方法,指定实现了结构的变量名(在插件代码中定义),得到Symbol
。 -
将
Symbol
转换为接口变量,此后可以通过接口调用插件的逻辑。
插件机制的应用场景
通过插件机制,主程序可以实现动态加载和更新模块(可理解成一段代码逻辑),而不用编译或重启主程序。
如果需要热更新,只需要实现多个 .so
文件的动态加载和切换即可。
插件机制的问题
-
系统限制:目前只在 Linux、Darwin 和 FreeBSD 上支持,在 Windows 上没有支持。
-
Go 版本限制:插件和主程序需要用同一个 Go 版本编译(编译参数也要一样),精确到小版本(如 1.20.14)。
-
依赖库限制:如果插件和主程序依赖了同一个库,两者用的库版本需要一样。
-
关闭机制:插件没有提供 Close 方法。如果不主动清理,插件创建的内存和 Goroutine 资源会一直存在(直至主程序退出)。
-
调试:在 Debug Go 程序时,无法跳转到插件内部的代码。