乐趣区

关于micro:go-micro-plugin

这篇文章中的 plugin 次要讲 https://github.com/micro/micro 中的插件,次要用于自定义网关中如何加载插件。(如文章[micro auth jwt])

go-micro 中的插件请见 https://github.com/micro/go-p…

官网 README 中有一些介绍

https://github.com/micro/micr…

官网示例:

在我的项目目录创立 plugin.go

package main

import (
    "log"
    "github.com/micro/cli/v2"
    "github.com/micro/micro/v2/plugin"
)

func init() {
    plugin.Register(plugin.NewPlugin(plugin.WithName("example"),
        plugin.WithFlag(cli.StringFlag{
            Name:   "example_flag",
            Usage:  "This is an example plugin flag",
            EnvVars: []string{"EXAMPLE_FLAG"},
            Value: "avalue",
        }),
        plugin.WithInit(func(ctx *cli.Context) error {log.Println("Got value for example_flag", ctx.String("example_flag"))
            return nil
        }),
    ))
}

最初编译

`go build -o micro ./main.go ./plugin.go`

一步步看看是怎么注册的,在 micro/plugin/manager.go 中

type manager struct {
    sync.Mutex
    plugins    []Plugin
    registered map[string]bool
}

var (
    // global plugin manager
    defaultManager = newManager())

func newManager() *manager {
    return &manager{registered: make(map[string]bool),
    }
}

plugin 包有默认 `defaultManager = newManager()`,是一个 manager{}对象

再来看plugin.NewPlugin()

// NewPlugin makes it easy to create a new plugin
func NewPlugin(opts ...Option) Plugin {return newPlugin(opts...)
}

func newPlugin(opts ...Option) Plugin {
    options := Options{
        Name: "default",
        Init: func(ctx *cli.Context) error {return nil},
    }

    for _, o := range opts {o(&options)
    }

    handler := func(hdlr http.Handler) http.Handler {
        for _, h := range options.Handlers {hdlr = h(hdlr)
        }
        return hdlr
    }

    return &plugin{
        opts:    options,
        handler: handler,
    }
}

做了以下事件:

  1. 初始化并设置 options
  2. 定义 handler 办法

    1. 顺次调用 options.Handlers
  3. 初始化并返回 plugin{}

    1. 初始化 plugin.handler 时,调用了第 2 步的 handler 办法,顺次调用了注册的 handler,(注册插件时传入的plugin.WithHandler(),例子放在最初)

最初是外层的plugin.Register()

// Register registers a global plugins
func Register(plugin Plugin) error {return defaultManager.Register(plugin)
}

func (m *manager) Register(plugin Plugin) error {m.Lock()
    defer m.Unlock()

    name := plugin.String()

    if m.registered[name] {return fmt.Errorf("Plugin with name %s already registered", name)
    }

    m.registered[name] = true
    m.plugins = append(m.plugins, plugin)
    return nil
}

做了以下事件:

  1. 获取插件名称,判断是否已注册
  2. manager.plugins 中增加以后 plugin

到这里插件的注册就实现了,那什么是被调用的,怎么失效的呢?

接下来看cmd.Init()

// Init initialised the command line
func Init(options ...micro.Option) {Setup(cmd.App(), options...)

    cmd.Init(cmd.Name(name),
        cmd.Description(description),
        cmd.Version(buildVersion()),
    )
}

func setup(app *ccli.App) {
    // 无关内容略...

    plugins := plugin.Plugins()

    for _, p := range plugins {if flags := p.Flags(); len(flags) > 0 {app.Flags = append(app.Flags, flags...)
        }

        if cmds := p.Commands(); len(cmds) > 0 {app.Commands = append(app.Commands, cmds...)
        }
    }

    before := app.Before

    app.Before = func(ctx *ccli.Context) error {

        // 无关内容略...
        for _, p := range plugins {if err := p.Init(ctx); err != nil {return err}
        }

        // 无关内容略...
    }
}

做了以下事件:

  1. 获取插件列表,收集所有参数
  2. 顺次调用所有插件的 Init() 办法

上面是一个有 plugin.WithHandler() 的插件例子

package main

import (
    "net/http"

    "myauth/lib/token"

    log "github.com/micro/go-micro/v2/logger"
    "github.com/micro/micro/v2/cmd"
    "github.com/micro/micro/v2/plugin"
)

func main() {tk := &token.Token{}
    tk.Init([]byte("key123456"))
    plugin.Register(plugin.NewPlugin(plugin.WithName("auth"),
        plugin.WithHandler(JWTAuthWrapper(tk),
        ),
    ))

    cmd.Init()}

func JWTAuthWrapper(t *token.Token) plugin.Handler {return func(h http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {log.Info("===========", r.URL.Path)
            // 不须要登录的 url 地址 strings.HasPrefix(r.URL.Path, "/hello") ||
            if r.URL.Path == "/myauth/Myauth/GetJwt" ||
                r.URL.Path == "/myauth/Myauth/InspectJwt" {h.ServeHTTP(w, r)
                return
            }

            // tokenstr := r.Header.Get("Authorization")// 当初不能够用 Authorization,须要用 Bearer
            tokenstr := r.Header.Get("Bearer")
            log.Info("tokenstr", tokenstr)
            userFromToken, e := t.Decode(tokenstr)
            log.Info("userFromToken", userFromToken)
            if e != nil {_, _ = w.Write([]byte("unauthorized"))
                return
            }

            // r.Header.Set("X-Example-Username", userFromToken.UserName)
            h.ServeHTTP(w, r)
            return
        })
    }
}

这个例子是一个自定义 micro 网关,拦挡申请查看 header 中的 jwt token 信息

总结

  1. 本篇介绍的是 micro 的插件应用及外部流程,次要用于自定义网关。不要和 go-micro 插件搞混同了。
  2. micro/plugin/build 下内容是对于 build 和 load 插件.so 的,不太欠缺这里就不深入研究了。
退出移动版