go micro plugin

這篇文章中的 plugin 主要講https://github.com/micro/micro 中的插件,主要用於自定義網關中如何加載插件。(如文章[micro auth jwt])git

go-micro中的插件請見https://github.com/micro/go-p...github

官方README中有一些介紹web

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

官方示例:app

在項目目錄建立plugin.goui

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
        }),
    ))
}

最後編譯url

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

一步步看看是怎樣註冊的,在micro/plugin/manager.go中code

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{}對象router

再來看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的,不太完善這裏就不深刻研究了。

go micro 分析系列文章
go micro server 啓動分析
go micro client
go micro broker
go micro cmd
go micro config
go micro store
go micro registry
go micro router
go micro runtime
go micro transport
go micro web
go micro registry 插件consul
go micro plugin
go micro jwt 網關鑑權
go micro 鏈路追蹤
go micro 熔斷與限流
go micro wrapper 中間件
go micro metrics 接入Prometheus、Grafana

相關文章
相關標籤/搜索