这篇文章中的 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,
}
}
做了以下事件:
- 初始化并设置 options
-
定义 handler 办法
- 顺次调用 options.Handlers
-
初始化并返回 plugin{}
- 初始化 plugin.handler 时,调用了第 2 步的 handler 办法,顺次调用了注册的 handler,(注册插件时传入的
plugin.WithHandler()
,例子放在最初)
- 初始化 plugin.handler 时,调用了第 2 步的 handler 办法,顺次调用了注册的 handler,(注册插件时传入的
最初是外层的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
}
做了以下事件:
- 获取插件名称,判断是否已注册
- 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}
}
// 无关内容略...
}
}
做了以下事件:
- 获取插件列表,收集所有参数
- 顺次调用所有插件的
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 信息
总结
- 本篇介绍的是 micro 的插件应用及外部流程,次要用于自定义网关。不要和 go-micro 插件搞混同了。
- micro/plugin/build 下内容是对于 build 和 load 插件.so 的,不太欠缺这里就不深入研究了。