go-plugin hashicorp開源的golang插件框架


go-plugin 已經存在很長時間了,同時hashicorp公司的好多產品都在使用vault,terrform,nomad,waypoint
詳細的介紹可以參考官方文檔,以下只是一個簡單的學習試用(demo來自官方)

項目准備

  • go mod
 
go mo init demo-plugin
go get github.com/hashicorp/go-plugin
go get github.com/hashicorp/go-hclog
  • 項目結構
├── Dockerfile
├── README.md
├── commons
│   └── greeter_interface.go
├── go.mod
├── go.sum
├── main.go
└── plugin
    └── greeter_impl.go

代碼說明

go-plugin 的開發模式,是基於二進制文件按需運行的(plugin 為二進制應用,懶加載運行)

  • plugin
    基於net-rpc模式開發的
    commons/greeter_interface.go 主要定義了關於plugin 接口的實現
 
package commons
import (
    "net/rpc"
    "github.com/hashicorp/go-plugin"
)
// Greeter greeter
type Greeter interface {
    Greet() string
}
// GreeterRPC Here is an implementation that talks over RPC
type GreeterRPC struct{ client *rpc.Client }
// Greet greet
func (g *GreeterRPC) Greet() string {
    var resp string
    // how to identifi
    err := g.client.Call("Plugin.Greet", new(interface{}), &resp)
    if err != nil {
        // You usually want your interfaces to return errors. If they don't,
        // there isn't much other choice here.
        panic(err)
    }
    return resp
}
// GreeterRPCServer Here is the RPC server that GreeterRPC talks to, conforming to
// the requirements of net/rpc
type GreeterRPCServer struct {
    // This is the real implementation
    Impl Greeter
}
// Greet greet for service impl
func (s *GreeterRPCServer) Greet(args interface{}, resp *string) error {
    *resp = s.Impl.Greet()
    return nil
}
// GreeterPlugin This is the implementation of plugin.Plugin so we can serve/consume this
//
// This has two methods: Server must return an RPC server for this plugin
// type. We construct a GreeterRPCServer for this.
//
// Client must return an implementation of our interface that communicates
// over an RPC client. We return GreeterRPC for this.
//
// Ignore MuxBroker. That is used to create more multiplexed streams on our
// plugin connection and is a more advanced use case.
type GreeterPlugin struct {
    // Impl Injection
    Impl Greeter
}
// Server server
func (p *GreeterPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
    return &GreeterRPCServer{Impl: p.Impl}, nil
}
// Client client
func (GreeterPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
    return &GreeterRPC{client: c}, nil
}

plugin/greeter_impl.go plugin 二進制程序的入口以及服務暴露

package main
import (
    "demo-plugin/commons"
    "os"
    "github.com/hashicorp/go-hclog"
    "github.com/hashicorp/go-plugin"
)
// Here is a real implementation of Greeter
type GreeterHello struct {
    logger hclog.Logger
}
func (g *GreeterHello) Greet() string {
    g.logger.Debug("message from GreeterHello.Greet")
    return "dalong demoapp!"
}
// handshakeConfigs are used to just do a basic handshake between
// a plugin and host. If the handshake fails, a user friendly error is shown.
// This prevents users from executing bad plugins or executing a plugin
// directory. It is a UX feature, not a security feature.
var handshakeConfig = plugin.HandshakeConfig{
    ProtocolVersion:  1,
    MagicCookieKey:   "BASIC_PLUGIN",
    MagicCookieValue: "hello",
}
func main() {
    logger := hclog.New(&hclog.LoggerOptions{
        Level:      hclog.Trace,
        Output:     os.Stderr,
        JSONFormat: true,
    })
    greeter := &GreeterHello{
        logger: logger,
    }
    // pluginMap is the map of plugins we can dispense.
    var pluginMap = map[string]plugin.Plugin{
        "greeter": &commons.GreeterPlugin{Impl: greeter},
    }
    logger.Debug("message from plugin", "foo", "bar")
    // 暴露plugin 的rpc 服務,懶運行機制
    plugin.Serve(&plugin.ServeConfig{
        HandshakeConfig: handshakeConfig,
        Plugins:         pluginMap,
    })
}
  • client (插件調用)
    main.go
 
package main
import (
    "demo-plugin/commons"
    "fmt"
    "log"
    "os"
    "os/exec"
    "github.com/hashicorp/go-hclog"
    "github.com/hashicorp/go-plugin"
)
func main() {
    // Create an hclog.Logger
    logger := hclog.New(&hclog.LoggerOptions{
        Name:   "plugin",
        Output: os.Stdout,
        Level:  hclog.Info,
    })
    // We're a host! Start by launching the plugin process.
    client := plugin.NewClient(&plugin.ClientConfig{
        HandshakeConfig: handshakeConfig,
        Plugins:         pluginMap,
        Cmd:             exec.Command("./plugin/greeter"),
        Logger:          logger,
    })
    defer client.Kill()
    // Connect via RPC
    rpcClient, err := client.Client()
    if err != nil {
        log.Fatal(err)
    }
    // Request the plugin
    raw, err := rpcClient.Dispense("greeter")
    if err != nil {
        log.Fatal(err)
    }
    // We should have a Greeter now! This feels like a normal interface
    // implementation but is in fact over an RPC connection.
    greeter := raw.(commons.Greeter)
    fmt.Println("from plugin message:" + greeter.Greet())
}
// handshakeConfigs are used to just do a basic handshake between
// a plugin and host. If the handshake fails, a user friendly error is shown.
// This prevents users from executing bad plugins or executing a plugin
// directory. It is a UX feature, not a security feature.
var handshakeConfig = plugin.HandshakeConfig{
    ProtocolVersion:  1,
    MagicCookieKey:   "BASIC_PLUGIN",
    MagicCookieValue: "hello",
}
// pluginMap is the map of plugins we can dispense.
var pluginMap = map[string]plugin.Plugin{
    "greeter": &commons.GreeterPlugin{},
}
  • Dockerfile
    all-in-one的部署運行
 
FROM golang:1.14-alpine AS build-env
RUN /bin/sed -i 's,http://dl-cdn.alpinelinux.org,https://mirrors.aliyun.com,g' /etc/apk/repositories
WORKDIR /go/src/app
COPY . .
ENV  GO111MODULE=on
ENV  GOPROXY=https://goproxy.cn
RUN apk update && apk add git \
    && go build -o ./plugin/greeter ./plugin/greeter_impl.go && go build -o basic .
FROM alpine:latest
RUN /bin/sed -i 's,http://dl-cdn.alpinelinux.org,https://mirrors.aliyun.com,g' /etc/apk/repositories
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
COPY --from=build-env /go/src/app/basic .
COPY --from=build-env /go/src/app/plugin/greeter ./plugin/
ENTRYPOINT [ "./basic" ]

運行

  • 構建
go build -o ./plugin/greeter ./plugin/greeter_impl.go
go build -o basic .
  • 運行
./basic
  • 效果

 

 

參考資料

https://github.com/hashicorp/go-plugin
https://github.com/hashicorp/go-plugin/tree/master/examples/basic
https://github.com/rongfengliang/go-plugin-learning


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM