Kratos Middleware是Kratos的核心一个中间件层,它是Kratos架构的核心部分,负责处理所有微服务间的通信和数据流。本文将深入了解Kratos Middleware的工作原理,更好地理解和使用Kratos。

使用示例

Kratos 内置了一系列的 middleware(中间件)用于处理 logging、 metrics 等通用场景, 您也可以通过实现 Middleware 接口,开发自定义 middleware,进行通用的业务处理,比如用户登录鉴权等。

// http
// 定义opts
var opts = []http.ServerOption{
    http.Middleware(
        recovery.Recovery(), // 把middleware按照需要的顺序加入
        tracing.Server(),
        logging.Server(),
    ),
}
// 创建server
http.NewServer(opts...)

自定义中间件的例子:

func Middleware1() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
            if tr, ok := transport.FromServerContext(ctx); ok {
                // Do something on entering
                defer func() {
                // Do something on exiting
                 }()
            }
            return handler(ctx, req)
        }
    }
}

链式调用

Kratos Middleware是通过链式结构实现的,其中每一个中间件都代表一个独立的处理单元,可以在请求的生命周期中的不同阶段执行不同的操作,如请求前的预处理、请求后的后处理等。

每一个中间件都是一个函数,具有相同的签名:

// Handler定义中间件调用的处理程序
type Handler func(ctx context.Context, req interface{}) (interface{}, error)

再看看/go-kratos/kratos/blob/main/middleware/middleware包中Middleware和Chain的实现,Middleware 是HTTP/gRPC传输中间件。 Chain返回一个中间件,它指定endpoint.的链式处理程序。

type Middleware func(Handler) Handler

func Chain(m ...Middleware) Middleware {
	return func(next Handler) Handler {
		for i := len(m) - 1; i >= 0; i-- {
			next = m[i](next)
		}
		return next
	}
}

Example,以https://github.com/go-kratos/kratos/blob/main/middleware/middleware_test.go为例:

func TestChain(t *testing.T) {
	next := func(ctx context.Context, req interface{}) (interface{}, error) {
		t.Log(req)
		i += 10
		return "reply", nil
	}

	got, err := Chain(test1Middleware, test2Middleware, test3Middleware)(next)(context.Background(), "hello kratos!")
	if err != nil {
		t.Errorf("expect %v, got %v", nil, err)
        }
  }
  
func test1Middleware(handler Handler) Handler {
	return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
		fmt.Println("test1 before")
		i++
		reply, err = handler(ctx, req)
		fmt.Println("test1 after")
		return
	}
}  
... 篇幅限制省略部分代码
OutPut
test1 before
test2 before
test3 before
    middleware_test.go:14: hello kratos!
test3 after
test2 after
test1 after

调用过程

对链式调用的使用有所了解后,接着来看Middleware 调用组织过程,大致归纳为:

  1. option方式配置中间件
  2. url路由匹配中间件
  3. 组装中间件添加到调用链
  4. 注册到gorilla,其中gorilla实现net.hander,设置为服务路由中间件

以Transport.Server Http服务为例,重点关注middleware和router 。

// Server is an HTTP server wrapper.
type Server struct {
	*http.Server
	middleware  matcher.Matcher
	router      *mux.Router
	...
}
// Matcher is a middleware matcher.
type Matcher interface {
	Use(ms ...middleware.Middleware)
	Add(selector string, ms ...middleware.Middleware)
	Match(operation string) []middleware.Middleware
}
  • Matcher是一个中间件匹配器,operation 为path,实现路由查找。
  • router 为gorilla.Mux, Http路由器和Url匹配器

一个完成的调用api-service:

func _Greeter_SayHello0_HTTP_Handler(srv GreeterHTTPServer) func(ctx http.Context) error {
	return func(ctx http.Context) error {
		var in HelloRequest
		if err := ctx.BindQuery(&in); err != nil {
			return err
		}
		if err := ctx.BindVars(&in); err != nil {
			return err
		}
		http.SetOperation(ctx, "/helloworld.v1.Greeter/SayHello")
		h := ctx.Middleware(func(ctx context.Context, req interface{}) (interface{}, error) {
			return srv.SayHello(ctx, req.(*HelloRequest))
		})
		out, err := h(ctx, &in)
		if err != nil {
			return err
		}
		reply := out.(*HelloReply)
		return ctx.Result(200, reply)
	}
}

其中ctx.Middleware的实现是:

func (c *wrapper) Middleware(h middleware.Handler) middleware.Handler {
	if tr, ok := transport.FromServerContext(c.req.Context()); ok {
		return middleware.Chain(c.router.srv.middleware.Match(tr.Operation())...)(h)
	}
	return middleware.Chain(c.router.srv.middleware.Match(c.req.URL.Path)...)(h)
} 

func (m *matcher) Match(operation string) []middleware.Middleware {
	ms := make([]middleware.Middleware, 0, len(m.defaults))
	if len(m.defaults) > 0 {
		ms = append(ms, m.defaults...)
	}
	if next, ok := m.matchs[operation]; ok {
		return append(ms, next...)
	}
	for _, prefix := range m.prefix {
		if strings.HasPrefix(operation, prefix) {
			return append(ms, m.matchs[prefix]...)
		}
	}
	return ms
}

以optionnal方式定义的中间件存储m.defaults,通过matcher.Match给不同的路由匹配不同的中间件。最终加入到 middleware.Chain的调用链实现链式调用。

小结

文本记录深入理解Kratos框架和Kratos Middleware,核心使用middleware.Chain的链式调用具有相同的签名的中间件函数。框架通过option的方式提供设置中间件, Matcher中间件匹配器匹配不同路由规则的中间件,来实现链式调用。

参考