References

Go Plugin

Bi-Directional Plugin

plugin-go-plugin-counter.svg

双向的实现,UserDefinedPlugin 可以包含一个公共的 Interface 实现,而 Interface 的实现在 Main Process 端。当 Main Process 获取到 UserDefinedPlugin 后,初始化该公共 Interface,再使用 Plugin 时,公共接口功能就由 Main Process 来实现了。

Start Plugin Process

plugin-plugin-start-procedure.svg

Main Process 需要创建一个 ClientConfig,用于关联 Plugin 的可执行文件,并关联感兴趣的 Plugin 实现。然后根据 ClientConfig 启动 Plugin Process。当 Plugin Process 启动后,会通过 Stdout 告知 Main Process,后续通信使用 NetRPC 还是 GRPC。

GRPC

Start Server

plugin-plugin-server-start.svg

在 Plugin 自定义实现中启动,启动过程比较简单,使用 plugin.Serve 即可,自定义 Plugin 的实现通过 map[string]plugin.Plugin 注册。

  1. func main() {
  2. plugin.Serve(&plugin.ServeConfig{
  3. HandshakeConfig: shared.Handshake,
  4. Plugins: map[string]plugin.Plugin{
  5. "kv": &shared.KVGRPCPlugin{Impl: &KV{}},
  6. },
  7. GRPCServer: plugin.DefaultGRPCServer,
  8. })
  9. }

注意要点
  • 除 Windows 操作系统外,Plugin 沟通全部通过本地 Unix Socket 来进行

    1. func serverListener() (net.Listener, error) {
    2. if runtime.GOOS == "windows" {
    3. return serverListener_tcp()
    4. }
    5. return serverListener_unix()
    6. }
  • 核心信息通过 Stdout 输出,由于 Main Process 是父进程,自然可以捕获子进程的 Stdout

    1. fmt.Printf("%d|%d|%s|%s|%s|%s\n",
    2. CoreProtocolVersion,
    3. protoVersion,
    4. listener.Addr().Network(),
    5. listener.Addr().String(),
    6. protoType,
    7. serverCert)

Register Custom Service

plugin-plugin-register-service.svg

plugin.Serve 关键代码部分,配置好内部使用的 Broker、Controller 服务后,将控制权转移给 GRPCPlugin,然后实现者可自行实现自己的代码逻辑。
GRPCPlugin 接口定义如下所示。GRPCServer 方法用于启动 GRPC 服务端功能,实现者可在此注册自定义的服务实现代码。GRPCClient 方法一般用于与 GRPCServer 中注册的服务对应的客户端功能实现。由于两个方法中均传入了 GRPCBroker,还可以做其他工作,后面会详细说明。

  1. type GRPCPlugin interface {
  2. // GRPCServer should register this plugin for serving with the
  3. // given GRPCServer. Unlike Plugin.Server, this is only called once
  4. // since gRPC plugins serve singletons.
  5. GRPCServer(*GRPCBroker, *grpc.Server) error
  6. // GRPCClient should return the interface implementation for the plugin
  7. // you're serving via gRPC. The provided context will be canceled by
  8. // go-plugin in the event of the plugin process exiting.
  9. GRPCClient(context.Context, *GRPCBroker, *grpc.ClientConn) (interface{}, error)
  10. }

Get RPC Client

plugin-plugin-client-client.svg

在程序主框架中,一般通过 plugin.Client 创建一个新的 Client 对象。并通过 Client.Client() 方法获取与自定义 Plugin 中 RPC 服务对应的客户端(RPC、gRPC)。 在客户端中,实现了 Broker、Controller 服务的调用。

Dispense

plugin-plugin-client-dispense.svg

Dispense 方法,最终触发了 GRPCPlugin 的 GRPCClient 方法,获取到自定义 Plugin 的客户端代码部分。后续可通过该方法获取的实现,做具体操作。

GRPC Broker

Proto

ConnInfo 消息定义如下

  1. message ConnInfo {
  2. uint32 service_id = 1;
  3. string network = 2;
  4. string address = 3;
  5. }

GRPCBroker 服务定义如下

  1. service GRPCBroker {
  2. rpc StartStream(stream ConnInfo) returns (stream ConnInfo);
  3. }

Landscape

plugin-grpc-broker-landscape.svg

plugin.GRPCServer 启动时,将 GRPCBrokerServer 注册到 grpc.Server 中,并调用 GRPCBroker.Run 方法启动 ConnInfo 收、发处理的协程。
plugin.GRPCClient 启动时,底层的 grpc.ClientConn 会发起到 grpc.Server 的连接,并调用 GRPCBroker.Run 方法启动 ConnInfo 收、发处理的协程。

Communication

plugin-grpc-broker-communication.svg

RPC

Server Main Procedure

plugin-rpc_server.svg

Mux Broker

plugin-rpc-mux-broker.svg

yamux.Session 会开启三个 Stream,分别为主控 Control Stream,标准输出 Stdout Stream 及标准错误 Stderr Stream。
Control Stream 用于发送 RPC 请求、应答。

Dispense

plugin-rpc-dispense.svg

MuxBroker 用于控制 yamux.Session 生成新的 Stream,新 Stream 共享底层 net.Conn 对象,也就是说,Client 与 Server 间只需要一个 Connection 就可以实现多个流。

Mattermost Plugin

Landscape

Core Component

plugin-landscape.svg

Communication

Plugin Process Start

plugin-hooks-plugin-process-start.svg

Plugin Installation

plugin-plugin-installation.svg