Go言語でプラグインの機構を使いたい
やりたいこととしては、下記の通り。
- LinuxにGoで建てたHTTPサーバをデーモンとして動かしたい
- HTTPサーバはプラグインを読み込んで機能の拡充が行える
- HTTPサーバは設定ファイルにプラグインファイルのパスを追加するだけで機能が拡充出来る
- プラグインを追加する度に本体をリビルドするのは嫌だ
どう実装するか
最初はHashiCorpが出しているGo言語でプラグインの機構を備えるライブラリを見ていたけども・・・。
https://github.com/hashicorp/go-plugin
このプラグインは、読み込むプラグインの数だけプロセスが立ち上がってしまう。
※何故ならば、このプラグインはRPCを使ってプラグインと疎通する設計のため、プラグイン側もプロセスを立ち上げておかないといけない。
じゃあどうするか
稼働させる場所は、Linuxサーバと決まっているため、もう共有ライブラリで良いんじゃね!?と思った次第。
共有ライブラリもビルド出来るしcgoを使えば何とかなりそう。
検証コード
共有ライブラリ(.so)側
hello関数を呼び出すと標準出力にhelloを表示するだけのどシンプルなものでございます。
hello.go
package main
import "C"
import "fmt"
//export hello
func hello() {
fmt.Println("hello")
}
func main() {
}
共有ライブラリをロードして使う側
main.go
package main
/*
#cgo LDFLAGS: -ldl
#include <dlfcn.h>
void call_func(void* p) {
void (*func)() = p;
func();
}
*/
import "C"
func main() {
handle := C.dlopen(C.CString("./shared/hello.so"), C.RTLD_LAZY)
if handle == nil {
panic("Failed to open shared object.")
}
defer C.dlclose(handle)
hello := C.dlsym(handle, C.CString("hello"))
if hello == nil {
panic("Could not found function pointer.")
}
C.call_func(hello)
}
検証してみる
共有ライブラリのビルド
$ mkdir shared
$ cd shared
$ go build -buildmode=c-shared -o hello.so hello.go
呼び出し側の確認
$ go run main.go
$ hello
ちゃんとhelloが標準出力に表示されていることを確認出来た。
一応Goでプラグインコードの実装が出来るからこれでも良いよね・・・。