当前位置: 首页 > 面试题库 >

Go插件依赖项如何工作?

须旭
2023-03-14
问题内容

Go 1.8支持Go插件。

我创建了两个插件,如下所示。

据我了解,该插件仅公开main包中的函数和变量。即plugin.Lookup()对于非main变量/函数将失败。

但是我想测试一个插件是否可以在内部从另一个插件调用方法,类似于C ++库如何调用另一个库。

所以我测试如下:

plugin1 github.com/vimal/testplugin

$ cat myplugin.go
package main

import "C"
import "fmt"
import help "github.com/vimal/testplugin1/plug"

func init() {
        fmt.Printf("main.init invoked\n")
}
// TestPlugin 
func TestPlugin() string {
        return help.Help()
}

plugin2 github.com/vimal/testplugin1

$ cat myplugin.go
package main

import "C"

func HelperFunc() string {
        return "help"
}
$ cat plug/helper.go
package help

func Help() string {
        return "help234"
}

这里的想法是,plugin1调用plugin2的内部非main功能。

主程序

主程序加载许多作为参数指定的插件,并TestPlugin()从最后一个插件调用。

测试1:

构建两个插件,加载两个插件,然后调用invoke
TestPlugin(),输出包含"help234",即调用内部函数。可以理解,因为两个插件都已加载,所以一个插件可以调用另一个插件的内部代码。

测试2:

仅加载plugin1,然后调用TestPlugin(),输出包含"help234",即调用内部函数。观察到与test1相同的输出。也许这次是从中找到该方法的GOPATH

测试3:

将文件夹重命名"github.com/vimal/testplugin1""github.com/vimal/junk1",删除plugin2,仅加载plugin1,然后调用TestPlugin()。输出仍然包含"help234",即调用内部函数。

我无法理解test3如何产生相同的输出。plugin1是否也包含plugin2代码?如何理解Go插件对其他Go插件的依赖性?

转到版本: go version go1.8rc3 linux/amd64


问题答案:

您没有完全按照自己的想法去做。

您的 plugin1 导入并使用一个 软件包 ,即github.com/vimal/testplugin1/plug。这与
plugin2 不相等!

这里发生的是,当您构建 plugin1时 ,其所有依赖项都被构建到插件文件中,包括.../testplugin1/plug软件包。并且当您加载
plugin1时 ,其所有依赖项也将从插件文件(包括plug软件包)中加载。此后,无论 plugin2 的加载状态如何
可以正常工作 也就不足为奇了 。这两个插件彼此独立。

-buildmode=plugin指令指示编译器要构建插件而不是独立应用程序,但这并不意味着必须不包含依赖项。它们必须是这样,因为插件不能完全保证Go应用程序将加载它,以及Go应用程序将拥有什么软件包。因为可运行的应用程序甚至仅包含应用程序本身明确引用的标准库中的程序包。

保证插件具有所需一切的唯一方法,并且如果它还包含其所有依赖项(包括来自标准库的依赖项),则它将可以正常工作。(这就是为什么构建简单的插件会生成相对较大的文件的原因,类似于构建简单的Go可执行文件会生成大文件的原因。)

几乎不需要添加到插件中的东西都包括Go运行时,例如,因为将要加载插件的正在运行的Go应用程序已经在运行Go运行时。(请注意,您只能从使用相同版本的Go编译的应用程序中加载插件。)但是除此之外,该插件还必须包含所需的一切。

Go是一种静态链接的语言。编译Go应用或插件后,它们将不再依赖或检查的值GOPATH,仅在构建它们时由Go工具使用。

更深刻的见解

您的主应用程序和插件可能引用相同的程序包(导入路径“相同”)。在这种情况下,将仅使用包装的一个“实例”。

如果此通用包具有“状态”,例如全局变量,则可以对此进行测试。让我们假设一个通用的共享软件包mymath

package mymath

var S string

func SetS(s string) {
    S = s
}

还有一个pg使用它的插件:

package main

import (
    "C"
    "mymath"
    "fmt"
)

func Start() {
    fmt.Println("pg:mymath.S", mymath.S)
    mymath.SetS("pghi")
    fmt.Println("pg:mymath.S", mymath.S)
}

以及使用mymath和加载pg(使用它)的主应用程序:

package main

import (
    "plugin"
    "mymath"
    "fmt"
)

func main() {
    fmt.Println("mymath.S", mymath.S)
    mymath.SetS("hi")
    fmt.Println("mymath.S", mymath.S)

    p, err := plugin.Open("../pg/pg.so")
    if err != nil {
        panic(err)
    }

    start, err := p.Lookup("Start")
    if err != nil {
        panic(err)
    }

    start.(func())()

    fmt.Println("mymath.S", mymath.S)
}

构建插件:

cd pg
go build -buildmode=plugin

运行主应用程序,输出为:

mymath.S 
mymath.S hi
pg:mymath.S hi
pg:mymath.S pghi
mymath.S pghi

分析:首先,主应用程序开始运行mymath.S,然后将其设置为"hi"最终。然后是插件,它会打印它(我们看到了主应用程序设置的值"hi"),然后将其更改为"pghi"。然后再次出现主应用程序并打印mymath.S,然后再次看到插件设置的最后一个值:"pghi"

因此,仅存在一个“实例”
mymath。现在,如果继续进行更改mymath,例如将重命名myMath.SetS()mymath.SetS2(),然后在主应用程序中将调用更新为mymath.SetS2("hi"),而无需重新构建插件,只需运行主应用程序,您将获得以下输出:

mymath.S 
mymath.S hi
panic: plugin.Open: plugin was built with a different version of package mymath

goroutine 1 [running]:
main.main()
    <GOPATH>/src/play/play.go:16 +0x4b5
exit status 2

如您所见,在构建主应用程序和插件时,将记录软件包版本(很可能是哈希),如果它们的导入路径在主应用程序和插件中匹配,则必须匹配。

(请注意,如果您不更改使用过的mymath包的导出标识符(和签名),而仅更改实现,则也会出现上述错误;例如func SetS(s string) { S = s + "+" }。)



 类似资料:
  • 最近,我发现了以下问题: 当我为我的项目设置依赖项管理时,我有一个child-pom,它使用具有依赖项的插件,我想要与在我的依赖项管理中声明的依赖项同步。 在根pom中,我在依赖项管理中声明: 在子pom中,我有一个插件需要gwt-user: 但是,如果我移除gwt-maven-plugin中使用的依赖版本,编译就会失败。 是不是还有别的办法可以实现呢? PS:在maven和maven插件中有一个

  • 问题内容: 我有一个项目,需要以下Maven jibx插件: 在jibx插件pom内部,有一个xpp3依赖关系,我想从我的项目构建过程中排除它(由于某种原因,我无法在私有存储库中拥有它)。 有没有一种方法可以配置我的pom.xml(而不是插件pom)来排除该依赖关系? 编辑:我试图从插件pom中删除xpp3依赖项,并且该项目可以成功构建,所以我知道依赖项不是强制性的。 问题答案: 这是一个示例,其

  • 我有一个gradle构建,它依赖于一个在buildSrc目录中编译的插件(MyTools)。这部分工作正常。我遇到的问题是试图从外部jar导入一个类以在myTools插件的源代码中使用。 我的目录结构如下: buildsrc ----build.gradle -----build.gradle -----settings.gradle -------libs -------Yuicompresso

  • 我正试图让maven下载所有的依赖项(编译、测试、插件等)。)这样我就可以避免让我们的dockerized构建浪费不必要的时间一遍又一遍地下载它们。 我们已经对maven build进行了dockerized,这样我们就可以从jenkins运行它,而无需在jenkins机器上安装大量构建特定的依赖项(Java、redis、maven依赖项等)。我们的构建依赖于增量docker构建,它只执行实际需要

  • 依赖 AdminLTE 依赖两个主要的框架。下载的软件包中已经包含这两个库,因此你不必手动下载它们。 Bootstrap 4 jQuery 3.3.1+ Popper.js 1.14.7+ 下面列出了所有其他插件 插件 AdminLTE 使用以下插件。相关文档,更新或许可证信息,请访问提供的链接。 你需要手动加载插件的 js/css 文件。 AdminLTE 不会自动加载所有插件,这会造成页面加载

  • 我目前正在使用Maven开发一个Java项目。在我的我收到了这个错误。 我添加了这种依赖性 到我的。但错误仍然是一样的。 我是否缺少为依赖项添加存储库?我也没有在我的。 有人能建议我在我的中添加的存储库代码吗?