golang 中的插件使用

golang 中 plugin 的使用

简介

  • golang 是一门静态编译的语言,想要实现动态热更新是相对比较困难的
  • go 1.8 版本之后,官方提供了 plugin 包来进行动态加载
  • 他可以动态的加载so和执行导出的方法,并且仅仅提供了两个方法:打开模块和提取符号,甚至连关闭都没有(-_-)。

使用场景

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

使用注意事项

  • Plguin 需要有自己的 main package
  • 编译的时候,使用 go build -buildmode=plugin file.go 来编译
  • 使用 plugin.Open(path string) 来打开.so文件,同一插件只能打开一次,重复打开会报错
  • 使用 plugin.LookUp(name string) 来获取插件中对外暴露的方法或者类型
  • 使用类型断言,断言后执行相应的方法

简单例子

插件

  • plugin.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    package main

    import "fmt"

    func main() {}

    func GetProductBasePrice(basePrice int) int {
    return basePrice + 200
    }
  • go build -buildmode=plugin plugin.go 命令编译出 plugin.so 插件

调用

  • main.go
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package main

    import (
    "fmt"
    "plugin"
    )

    func main() {

    p, err := plugin.Open("plugin.so")
    if err != nil {
    panic(err)
    }
    m, err := p.Lookup("GetProductBasePrice")
    if err != nil {
    panic(err)
    }
    res := m.(func(int) int)(30)
    fmt.Println(res)
    }

注意几点问题:

  • 插件中定义的 struct 无法暴露出来,可以让主程序和插件程序import公共的 package 来解决
  • 私有方法、变量不会被暴露出来