本文章总结Go语言编程中常用的插件系统设计。
Go 语言自带 plugin package
Go 1.8 开始自带 plugin 模块,通过 go build -buildmode=plugin 将代码打包成动态链接库; 然后在主应用程序中通过 plugin.Open,plugin.Lookup 获取插件实例。
基于 package init 的编译型插件
Go 语言有个特别的特性就是当 import 某个 package 时,会立刻执行该 package 的 init 函数;这个特性通常不推荐使用,因为会导致代码执行逻辑难以阅读;但该特性十分适合用于实现编译型的插件,譬如 sql 库的各种驱动可以通过 init 函数注册:
func init() {
sql.Register("mysql", &MySQLDriver{})
}
又譬如 Caddy 扩展:
func init() {
caddy.RegisterModule(Gizmo{})
}
使用 init 函数的优点是只有用到的插件才会编译,并且插件会编译到主程序执行文件中,符合 go 易于部署的特点。
基于 gRPC 的独立运行插件
由于 go 语言网络相关的组件非常丰富,因此实现一个基于网络的插件系统也非常简单,譬如 gRPC。https://github.com/hashicorp/go-plugin 项目是一个开源的基于gRPC的插件系统;与前面介绍的两种不同,基于gRPC的插件系统的优势是插件可以用多种编程语言编写,并且可以将插件对主程序的调用限定在公开接口上,并且可以通过 Docker 限制插件对系统资源的占用,避免某个插件把系统搞崩溃。