概览

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

内置中间件

相关代码均可以在middleware目录下找到。

  • logging: 用于请求日志的记录。
  • metrics: 用于启用metric。
  • recovery: 用于recovery panic。
  • tracing: 用于启用trace。
  • validate: 用于处理参数校验。
  • metadata: 用于启用元信息传递
  • auth: 用于提供基于JWT的认证请求
  • ratelimit: 用于服务端流量限制
  • circuitbreaker: 用于客户端熔断控制

生效顺序

一个请求进入时的处理顺序为Middleware注册的顺序,而响应返回的处理顺序为注册顺序的倒序。

  1. ┌───────────────────┐
  2. MIDDLEWARE 1
  3. ┌────────────────┐│
  4. MIDDLEWARE 2 ││
  5. ┌─────────────┐││
  6. MIDDLEWARE 3 │││
  7. ┌─────────┐ │││
  8. REQUEST YOUR │││ RESPONSE
  9. ──────┼─┼─┼─▷ HANDLER ○─┼┼┼───▷
  10. └─────────┘ │││
  11. └─────────────┘││
  12. └────────────────┘│
  13. └───────────────────┘

使用中间件

NewGRPCServerNewHTTPServer中通过ServerOption进行注册。 如

  1. // http
  2. // 定义opts
  3. var opts = []http.ServerOption{
  4. http.Middleware(
  5. recovery.Recovery(), // 把middleware按照需要的顺序加入
  6. tracing.Server(),
  7. logging.Server(),
  8. ),
  9. }
  10. // 创建server
  11. http.NewServer(opts...)
  12. //grpc
  13. var opts = []grpc.ServerOption{
  14. grpc.Middleware(
  15. recovery.Recovery(), // 把middleware按照需要的顺序加入
  16. tracing.Server(),
  17. logging.Server(),
  18. ),
  19. }
  20. // 创建server
  21. grpc.NewServer(opts...)

自定义中间件

需要实现Middleware接口。 中间件中您可以使用tr, ok := transport.FromServerContext(ctx)获得Transporter实例以便访问接口相关的元信息

基本的代码模板

  1. import (
  2. "context"
  3. "github.com/go-kratos/kratos/v2/middleware"
  4. "github.com/go-kratos/kratos/v2/transport"
  5. )
  6. func Middleware1() middleware.Middleware {
  7. return func(handler middleware.Handler) middleware.Handler {
  8. return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
  9. if tr, ok := transport.FromServerContext(ctx); ok {
  10. // Do something on entering
  11. defer func() {
  12. // Do something on exiting
  13. }()
  14. }
  15. return handler(ctx, req)
  16. }
  17. }
  18. }

定制中间件

对特定路由定制中间件

  • server:selector.Server(ms...)
  • client: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

  1. import "github.com/go-kratos/kratos/v2/middleware/selector"
  2. http.Middleware(
  3. selector.Server(recovery.Recovery(), tracing.Server(),testMiddleware).
  4. Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello").
  5. Regex(`/test.hello/Get[0-9]+`).
  6. Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/").
  7. Build(),
  8. )

http client

  1. import "github.com/go-kratos/kratos/v2/middleware/selector"
  2. http.WithMiddleware(
  3. selector.Client(recovery.Recovery(), tracing.Server(),testMiddleware).
  4. Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello").
  5. Regex(`/test.hello/Get[0-9]+`).
  6. Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/").
  7. Match(func(ctx context.Context,operation string) bool {
  8. if strings.HasPrefix(operation, "/go-kratos.dev") || strings.HasSuffix(operation, "world") {
  9. return true
  10. }
  11. tr, ok := transport.FromClientContext(ctx)
  12. if !ok {
  13. return false
  14. }
  15. if tr.RequestHeader().Get("go-kratos") == "kratos" {
  16. return true
  17. }
  18. return false
  19. }).Build(),
  20. )

grpc server

  1. import "github.com/go-kratos/kratos/v2/middleware/selector"
  2. grpc.Middleware(
  3. selector.Server(recovery.Recovery(), tracing.Server(),testMiddleware).
  4. Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello").
  5. Regex(`/test.hello/Get[0-9]+`).
  6. Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/").
  7. Build(),
  8. )

grpc client

  1. import "github.com/go-kratos/kratos/v2/middleware/selector"
  2. grpc.Middleware(
  3. selector.Client(recovery.Recovery(), tracing.Server(),testMiddleware).
  4. Path("/hello.Update/UpdateUser", "/hello.kratos/SayHello").
  5. Regex(`/test.hello/Get[0-9]+`).
  6. Prefix("/kratos.", "/go-kratos.", "/helloworld.Greeter/").
  7. Build(),
  8. )

注意: 定制中间件是通过 operation 匹配,并不是http本身的路由!!!

operation 是 HTTP 及 gRPC 统一的 gRPC path

operation查找

gRPC path 的拼接规则为 /包名.服务名/方法名

比如在如下 proto 文件中,我们要调用 SayHello 这个方法,那么 operation 就为 /helloworld.Greeter/SayHello

  1. syntax = "proto3";
  2. package helloworld;
  3. import "google/api/annotations.proto";
  4. option go_package = "github.com/go-kratos/examples/helloworld/helloworld";
  5. // The greeting service definition.
  6. service Greeter {
  7. // Sends a greeting
  8. rpc SayHello (HelloRequest) returns (HelloReply) {
  9. option (google.api.http) = {
  10. get: "/helloworld/{name}",
  11. };
  12. }
  13. }
  14. // The request message containing the user's name.
  15. message HelloRequest {
  16. string name = 1;
  17. }
  18. // The response message containing the greetings
  19. message HelloReply {
  20. string message = 1;
  21. }