前言
最近在學習fabric 1.2版本的新特性,其中有一個是實現了交易背書和區塊結果驗證這兩個原本由系統鏈碼escc
和vscc
負責的模組的可插拔。它們的可插拔用到了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{}
,Symbol
是interface
的別名,也就是說,我們可以從外掛程式裡面拿到任何類型的可匯出元素。
小試牛刀
瞭解了plugin
包的準系統,按照慣例,我們要用hello world
檢驗下。
準備plugin源碼pluginhello.go
:
package mainimport ( "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 lsinvokeplugin.go pluginhello.go pluginhello.so
用go build
命令,同時制定buildmode
為plugin
即可。So Easy!
注意:這裡尤其要注意的是,plugin的源碼需要在main
包中,否則無法編譯。
下面該調用這個外掛程式了:
package mainimport ( "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.goHello World From Plugin!
完美調用了外掛程式!
說明: Go 1.8時 Plugin 支援Linux和macOS,但是因為bug在1.9取消了對macOS的支援,1.10時又恢複了對macOS的支援。
我的機器上因為裝了多個版本,而開發需要常用的是go 1.9,所以這裡使用go 1.10時,命令用的go1.10
這個軟串連