基于Go插件系统的惯用方法

基于Go插件系统的惯用方法,go,Go,我有一个Go项目,我想开源,但有些元素不适合OSS,例如公司特定的逻辑等 我设想了以下方法: 接口在核心存储库中定义 插件可以是独立的存储库,其类型实现了在core中定义的接口。这允许插件被安置在完全独立的模块中,因此有自己的CI作业等 插件通过符号链接编译成最终的二进制文件 这将产生如下目录结构: |- $GOPATH |- src |- github.com |- jabclab |- core-system |- plugin

我有一个Go项目,我想开源,但有些元素不适合OSS,例如公司特定的逻辑等

我设想了以下方法:

  • 接口
    在核心存储库中定义
  • 插件可以是独立的存储库,其
    类型实现了在core中定义的
    接口。这允许插件被安置在完全独立的模块中,因此有自己的CI作业等
  • 插件通过符号链接编译成最终的二进制文件
这将产生如下目录结构:

|- $GOPATH
  |- src
    |- github.com
      |- jabclab
        |- core-system
          |- plugins <-----|
      |- xxx               | 
        |- plugin-a ------>| ln -s
      |- yyy               |  
        |- plugin-b ------>|
我不确定的一个问题是,如何使插件中定义的类型在核心运行时可用。我宁愿不使用,但目前想不出更好的方法。如果我在一次回购中执行代码,我会使用类似以下内容:

package plugins

type Plugin interface {
  Exec(chan<- string) error
}

var Registry map[string]Plugin

// plugin_a.go
func init() { Registry["plugin_a"] = PluginA{} }

// plugin_b.go
func init() { Registry["plugin_b"] = PluginB{} }
包插件
类型插件接口{

Exec(chan这是我在Go中最喜欢的问题之一。我有一个开源项目也必须处理这个问题(),它有可插拔的DB和运行时(Docker、k8s、Mesos等)客户端。在Go主分支中的插件包之前(因此它应该很快就会稳定发布)我只是将所有插件编译成二进制文件,并允许配置决定使用哪个插件

对于插件包,您可以对插件使用动态链接,这与C的
dlopen()
加载非常相似,而go的插件包的行为在文档中也有很好的描述

另外,我建议您看看Hashicorp是如何通过在本地unix套接字上执行RPC来解决这个问题的

像Hashicorp的模型一样,将插件作为单独的进程运行的另一个好处是,在插件失败的情况下,您可以获得极大的稳定性,但主进程能够处理该失败

我还应该提到Docker在Go中也有类似的插件,除了Docker使用HTTP而不是RPC。此外,Docker工程师在过去曾发布过关于为动态逻辑嵌入Javascript解释器的帖子

我想指出的问题是,评论中提到的sql包的模式并不是一个插件架构,实际上,你仍然局限于你的导入中的任何内容,因此你可以有多个main.go,但这不是一个插件,插件的要点是,同一个程序可以运行任何一段代码sql包之类的东西所具有的灵活性是,一个单独的包可以确定要使用的DB驱动程序。尽管如此,最终还是要修改代码来更改正在使用的驱动程序


我想补充的是,除了编译成相同的二进制文件并使用配置进行选择之外,所有这些插件模式都可以有自己的构建、测试和部署(即各自的CI/CD),但不一定如此。

为什么不像
数据库/sql
?拥有“核心”(即
数据库/sql
),拥有自己的“插件”(例如,
github.com/lib/pq
),让你的“main”导入你想要使用的“核心”和所有“插件”。不需要符号链接。你可以让
生成一个导入插件的主文件,并在每个插件的
init()
函数中将其注册到主存储库中。导入,即使是
import\u“foo/bar/plugin”
将导致init func在start上运行并注册插件。请参阅以下答案@Not_a_Golfer谢谢:-)这种方法是否允许外部插件独立编译?它们是否会因为注册表变量不存在而编译失败?如果不是,我猜
//go:generate
可能是(a) 在插件本身中,或者(b)在核心应用程序中,然后在
import\uuu“foo/bar/plugin上进一步构建“
,它还将生成插件的
类型添加到插件注册表中。reddit上的此线程详细说明了插件架构的选项。Go lang在其进一步发布的版本中将具有允许外部加载插件的构建模式。