深入了解KratosMiddlewar的实现原理
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 调用组织过程,大致归纳为:
- option方式配置中间件
- url路由匹配中间件
- 组装中间件添加到调用链
- 注册到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中间件匹配器匹配不同路由规则的中间件,来实现链式调用。
参考
- kratos doc https://go-kratos.dev/docs/component/middleware/overview
- kratos middleware pkg https://github.com/go-kratos/kratos/blob/main/middleware/middleware.go