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

posted on 2020-10-20 17:35  荣锋亮  阅读(3093)  评论(0编辑  收藏  举报

导航