关于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的,不太欠缺这里就不深入研究了。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理