这篇文章中的 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的,不太完善这里就不深入研究了。
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