Kratos 内置了一系列的 middleware(中间件)用于处理 logging、 metrics 等通用场景。您也可以通过实现 Middleware 接口,开发自定义 middleware,进行通用的业务处理,比如用户登录鉴权等。
相关代码均可以在 middleware
目录下找到。
logging
: 用于请求日志的记录。metrics
: 用于启用 metric。recovery
: 用于 recovery panic。tracing
: 用于启用 trace。validate
: 用于处理参数校验。metadata
: 用于启用元信息传递。auth
: 用于提供基于 JWT 的认证请求。ratelimit
: 用于服务端流量限制。circuitbreaker
: 用于客户端熔断控制。一个请求进入时的处理顺序为 Middleware 注册的顺序,而响应返回的处理顺序为注册顺序的倒序,即先进后出(FILO)。
┌───────────────────┐ │MIDDLEWARE 1 │ │ ┌────────────────┐│ │ │MIDDLEWARE 2 ││ │ │ ┌─────────────┐││ │ │ │MIDDLEWARE 3 │││ │ │ │ ┌─────────┐ │││REQUEST │ │ │ │ YOUR │ │││ RESPONSE ──────┼─┼─┼─▷ HANDLER ○─┼┼┼───▷ │ │ │ └─────────┘ │││ │ │ └─────────────┘││ │ └────────────────┘│ └───────────────────┘
在 NewGRPCServer
和 NewHTTPServer
中通过 ServerOption
进行注册。
例如:
// http// 定义optsvar opts = []http.ServerOption{ http.Middleware( recovery.Recovery(), // 把middleware按照需要的顺序加入 tracing.Server(), logging.Server(), ),}
// 创建serverhttp.NewServer(opts...)
//grpcvar opts = []grpc.ServerOption{ grpc.Middleware( recovery.Recovery(), // 把middleware按照需要的顺序加入 tracing.Server(), logging.Server(), ),}
// 创建servergrpc.NewServer(opts...)
需要实现 Middleware
接口。
中间件中您可以使用 tr, ok := transport.FromServerContext(ctx)
获得 Transporter 实例以便访问接口相关的元信息。
基本的代码模板:
import ( "context"
"github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/transport")
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) } }}
对特定路由定制中间件:
selector.Server(ms...)
selector.Client(ms...)
匹配规则(多参数):
Path(path...)
: 路由匹配Regex(regex...)
: 正则匹配Prefix(prefix...)
: 前缀匹配Match(fn)
: 函数匹配,函数格式为func(ctx context.Context,operation string) bool
。 operation
为 path,函数返回值为true
,匹配成功,ctx
可使用transport.FromServerContext(ctx)
或者 transport.FromClientContext(ctx
获取 Transporter)
。http server
import "github.com/go-kratos/kratos/v2/middleware/selector"
http.Middleware( selector.Server(recovery.Recovery(), tracing.Server(),testMiddleware). Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello"). Regex(`/test.hello/Get[0-9]+`). Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/"). Build(), )
http client
import "github.com/go-kratos/kratos/v2/middleware/selector"
http.WithMiddleware( selector.Client(recovery.Recovery(), tracing.Server(),testMiddleware). Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello"). Regex(`/test.hello/Get[0-9]+`). Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/"). Match(func(ctx context.Context,operation string) bool { if strings.HasPrefix(operation, "/go-kratos.dev") || strings.HasSuffix(operation, "world") { return true } tr, ok := transport.FromClientContext(ctx) if !ok { return false } if tr.RequestHeader().Get("go-kratos") == "kratos" { return true } return false }).Build(), )
grpc server
import "github.com/go-kratos/kratos/v2/middleware/selector"
grpc.Middleware( selector.Server(recovery.Recovery(), tracing.Server(),testMiddleware). Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello"). Regex(`/test.hello/Get[0-9]+`). Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/"). Build(), )
grpc client
import "github.com/go-kratos/kratos/v2/middleware/selector"
grpc.Middleware( selector.Client(recovery.Recovery(), tracing.Server(),testMiddleware). Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello"). Regex(`/test.hello/Get[0-9]+`). Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/"). Build(), )
注意: 定制中间件是通过 operation 匹配,并不是 http 本身的路由!!!
operation 是 HTTP 及 gRPC 统一的 gRPC path。
operation 查找
gRPC path 的拼接规则为 /包名.服务名/方法名(/package.Service/Method)
。
比如在如下 proto 文件中,我们要调用 SayHello 这个方法,那么 operation 就为 /helloworld.Greeter/SayHello
。
syntax = "proto3";
package helloworld;
import "google/api/annotations.proto";
option go_package = "github.com/go-kratos/examples/helloworld/helloworld";
// The greeting service definition.service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) { option (google.api.http) = { get: "/helloworld/{name}", }; }}// The request message containing the user's name.message HelloRequest { string name = 1;}
// The response message containing the greetingsmessage HelloReply { string message = 1;}