39

Golang笔记-Plugin初探

 5 years ago
source link: https://studygolang.com/articles/13871?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

最近在学习fabric 1.2版本的新特性,其中有一个是实现了交易背书和区块结果验证这两个原本由系统链码 esccvscc 负责的模块的可插拔。它们的可插拔用到了Go的plugin技术,这也是我第一次知道Go Plugin的概念(虽然在Go 1.8版本就有了),于是准备探一探究竟Go Plugin是什么,怎么用。

什么是Go Plugin

Golang是静态编译型语言,在编译时就将所有引用的包(库)全部加载打包到最终的可执行程序(或库文件)中,因此并不能在运行时动态加载其他共享库。Go Plugin提供了这样一种方式,能够让你在运行时动态加载外部功能。

为什么用Go Plugin

其实应该问为什么要用Plugin,我觉得原因有很多,比如:

  • 可插拔:有了Plugin,我的程序可以根据需要随时替换其中某些部件而不用修改我的程序;
  • 动态加载的需要:有些模块只有在运行时才能确定,需要动态加载外部的功能模块;
  • 独立开发:Plugin 可以和主程序独立建设,主程序只需要制定好框架,实现默认(模版)功能。Plugin 可根据用户需求随时自行扩展开发,运行时随意替换,提高了程序的可定制性;

怎么用Go plugin

Golang 对 Plugin 的实现在标准库 plugin 中。整个接口可以说相当简洁了。

type Plugin struct{ ... }
    func Open(path string) (*Plugin, error)
    func (p *Plugin) Lookup(symName string) (Symbol, error)
type Symbol interface{}

是的,你没有看错,就只有两个 type 和两个方法。

Plugin

type Plugin 即Golang加载的插件,与之有关的两个方法:

  • Open : 根据参数 path 提供的插件路径加载这个插件,并返回插件这个插件结构的指针 *Glugin
  • Lookup : *Plugin 的惟一方法,通过名称 symName 在插件中寻找对应的变量或方法,以 Symbol 的形式返回

Symbol

根据定义 type Symbol interface{}Symbolinterface 的别名,也就是说,我们可以从插件里面拿到任何类型的可导出元素。

小试牛刀

了解了 plugin 包的基本功能,按照惯例,我们要用 hello world 检验下。

准备plugin源码 pluginhello.go

package main

import (
    "fmt"
)

func Hello() {
    fmt.Println("Hello World From Plugin!")
}

这里在插件中,定义了一个可导出方法 Hello 打印 Hello World From Plugin!

有了源码,怎样将他编译成一个插件呢?

➜  plugin go1.10 build --buildmode=plugin -o pluginhello.so pluginhello.go
➜  plugin ls
invokeplugin.go pluginhello.go  pluginhello.so

go build 命令,同时制定 buildmodeplugin 即可。 So Easy!

注意:这里尤其要注意的是,plugin的源码需要在 main 包中,否则无法编译。

下面该调用这个插件了:

package main

import (
    "fmt"
    "os"
    "plugin"
)

func main() {
    p, err := plugin.Open("./pluginhello.so")
    if err != nil {
        fmt.Println("error open plugin: ", err)
        os.Exit(-1)
    }
    s, err := p.Lookup("Hello")
    if err != nil {
        fmt.Println("error lookup Hello: ", err)
        os.Exit(-1)
    }
    if hello, ok := s.(func()); ok {
        hello()
    }
}

首先通过 Open 方法打开插件,然后通过名称 Hello 找到插件中的 func Hello 方法。

注意,由于从插件中找到的任何元素都是以 Symbol 形式(即 interface{} )返回,我们需要通过断言的形式对结果进行判断和转换,得到我们需要的类型。

让我们看看效果吧:

➜  plugin go1.10 run invokeplugin.go
Hello World From Plugin!

完美调用了插件!

说明: Go 1.8时 Plugin 支持Linux和macOS,但是因为bug在1.9取消了对macOS的支持,1.10时又恢复了对macOS的支持。

我的机器上因为装了多个版本,而开发需要常用的是go 1.9,所以这里使用go 1.10时,命令用的 go1.10 这个软连接


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK