背景
在我做 C 語言開發的時候,為了讓程式有更好的擴充性,通常選擇将需要擴充的功能實作為插件,通過加載 so 檔案的方式導入插件中的函數。當我學 Golang 的時候,很希望能有這樣的插件功能。終于,Golang 在 1.8 版本的時候支援了插件功能。于是,第一時間嘗鮮,并寫了個開源庫來支援熱更新插件,代碼位址在文末。
環境
系統: linux (别問為什麼,因為 windows 下 Golang不支援動态庫)
Golang 版本: 1.5 以上支援動态庫,1.8 以上支援 plugin
插件代碼
插件代碼跟普通的 Golang 子產品代碼沒啥差别,主要是 package 必須是 main。下面是一段簡易的插件代碼
//testplugin.go
package main
import (
"fmt"
)
func init() {
fmt.Println("world")
//我們還可以做其他更高階的事情,比如 platform.RegisterPlugin({"func": Hello}) 之類的,向插件平台自動注冊該插件的函數
}
func Hello() {
fmt.Println("hello")
}
init 函數的目的是在插件子產品加載的時候自動執行一些我們要做的事情,比如:自動将方法和類型注冊到插件平台、輸出插件資訊等等。
Hello 函數則是我們需要在調用方顯式查找的 symbol
編譯指令
go build -buildmode=plugin testplugin.go
編譯完後我們可以看到目前目錄下有一個 testplugin.so 檔案
我們也可以通過類似如下指令來生成不同版本的插件
go build -o testplugin_v1.so -buildmode=plugin testplugin.go
如果要想更好的控制插件的版本,想做更酷的事情,比如:熱更新插件。那麼可以采用自動注冊的方式,新版本的插件加載上來後,自動注冊插件版本号,插件平台裡優先使用新版本的方法。
使用
使用方需要引入 plugin 這個包
//main.go
package main
import (
"plugin"
)
func main() {
p, err := plugin.Open("testplugin.so")
if err != nil {
panic(err)
}
f, err := p.Lookup("Hello")
if err != nil {
panic(err)
}
f.(func())()
}
輸出
$ go run main.go
world
hello
我們可以看到,我們隻顯式調用了插件中的 Hello 方法,列印 hello 這個字元串,但是在調用 Hello 之前,已經輸出了 world,這個正是插件 init 函數做的事情。
總結
Golang 支援插件使得 Golang 程式的擴充性上升到另一個台階,可以用來做更酷的事情,如:利用插件做服務的熱更新
代碼:https://github.com/letiantech/hotplugin
推薦文章
如何用Go打造千萬級流量秒殺系統