Golang 中实现插件机制的方法对比

我们知道类似 Java 等半编译半解释型语言编译生成的都是类似中间态的字节码,所以在 Java 里面我们想要实现程序工作的动态扩展,可以通过 Java 的字节码编辑技术 ASM,并结合 JVM 的 字节码动态加载技术来实现动态修改和加载字节码。

但是 Golang 是编译型语言,编译后直接生成的是可执行文件,但是如果我们又需要在不发布版本的情况下实现程序功能的动态扩展,我们可以通过什么方式来实现呢?下面主要介绍 Golang 里面实现程序功能动态扩展的两种方式:Golang 原生插件和 go-plugin 插件。

一、Golang 原生插件


Golang 原生插件使用流程如下:

将代码编译成 .so 文件

package main  
  
const Name = "PluginName"  
  
func GetName() string {  
    return Name  
}

将以上代码使用 go build -buildmode=plugin 命令即可编译成插件。

使用插件示例

func main() {  
    // 加载插件
    open, err := plugin.Open("~/plg.so")  
    if err != nil {  
        panic(err)  
    }
    // 查找标识符
    lookup, err := open.Lookup("GetName")  
    if err != nil {  
        panic(err)  
    }  
    res := lookup.(func() string)()  
    fmt.Printf("%v", res)  
}

原生插件的弱点

  1. 编译的 Go 版本必须完全一致 - 事实上这个插件都可以不是由同一个人编写,要求编译的 Go 版本一致显然有点要求太高了
  2. 双方依赖的公共第三方库版本必须完全一致
  3. GOPATH 也得保持一致 - 不过这一点可以在编译时候使用 trimpath 参数解决
  4. 插件加载之后无法卸载

二、go-plugin


鉴于 Golang 原生插件框架的问题,所以包括 Terraform、Grafana 等系统使用的是 go-plugin 这个框架。go-plugin 使用流程如下。

⚠️ 使用的时候注意复用客户端,因为每次初始化实际上是启动了一个子进程,这样会消耗非常多的内存和 CPU 资源。

我们看 go-plugin 的使用流程,可以发现其实 go-plugin 是通过在应用内部启动一个服务端子进程,应用通过 rpc 的方式和服务端子进程进行交互来实现插件的动态加载。这样其实几乎可以作为一个编译型语言实现动态加载的通用方案,实际上不是对应用本身做了扩展,而是对应用依赖的接口做了相关的扩展。

go-plugin 的具体使用可以参考文章最后的链接。

三、参考


本文亦通过 NoOne’s Blog 发表。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值