grpc-interceptor

  1. grpc中的拦截器
    1. proto
    2. clinet
    3. server
  2. 使用场景
    1. proto
    2. server
    3. client

grpc中的拦截器

proto

syntax = "proto3";

option go_package = ".;proto";

service Greeter{
  rpc SayHello(HelloRequest) returns (HelloResponse); // Hello 的接口
}


message HelloRequest {
  string name = 1; // 1 是编号,他不是值!
}

message HelloResponse {
  string  message = 1;
}
protobuf复制代码

clinet

package main

import (
    "context"
    "fmt"
    "go-grpc/grpc_interpretor/proto"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/grpc/metadata"
    "time"
)

const (
    timestampFormat = time.StampNano
)

// 调用的话 直接 拷贝一份 .proto 文件
// 然后 生成相关的代码  【千万千万不要修改代码!】

func main() {
    // client 端的拦截器
    interceptor := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
        start := time.Now()
        err := invoker(ctx, method, req, reply, cc)
        fmt.Println("耗时---> %s", time.Since(start))
        return err
    }

    DialOption := grpc.WithUnaryInterceptor(interceptor) // 返回 DialOption

    // DialOption的一种写法
    var opts []grpc.DialOption
    opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
    opts = append(opts, DialOption)

    conn, err := grpc.NewClient("127.0.0.1:8888", opts...)
    if err != nil {
        panic(err)
    }
    defer conn.Close()

    c := proto.NewGreeterClient(conn)

    // metadata
    // New 和 Pairs 都可以
    md := metadata.Pairs(
        "timestamp", time.Now().Format(timestampFormat),
        "name", "LiShun",
        "age", "21")

    //md := metadata.New(map[string]string{
    //	"timestamp": time.Now().Format(timestampFormat),
    //})

    // 会返回一个 context.Background()
    ctx := metadata.NewOutgoingContext(context.Background(), md)

    // 调用
    res, err := c.SayHello(ctx, &proto.HelloRequest{
        Name: "LiShun",
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(res.Message)
}
go复制代码

使用场景:

  • 请求失败的话再次重试

server

package main

import (
    "context"
    "fmt"
    "go-grpc/grpc_interpretor/proto"
    "google.golang.org/grpc"
    "google.golang.org/grpc/metadata"
    "log"
    "net"
)

type Server struct {
    proto.UnimplementedGreeterServer // 嵌入这个方法,这样我们就不需要手动实现了
}

// 限制死,context可以限制时间

func (s *Server) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloResponse, error) {
    // 检验 metadata
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        fmt.Println("get metadata failed", ok)
        return &proto.HelloResponse{Message: "校验未通过"}, nil
    } // 校验未通过

    // 校验通过
    for key, val := range md {
        fmt.Println(key, val)
    }

    return &proto.HelloResponse{
        Message: "hello " + req.Name + " " + md.Get("timestamp")[0],
    }, nil
}

func main() {
    // 拦截器函数
    interceptor := func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
        fmt.Println("接受到了一个新的请求")
        res, err := handler(ctx, req)
        fmt.Println("请求已经完成")
        return res, err
    }

    // 生成拦截器
    opt := grpc.UnaryInterceptor(interceptor) // 返回 ServerOption

    // 1. 实例化一个 grpc 的 server
    g := grpc.NewServer(opt)
    // 2. 注册
    proto.RegisterGreeterServer(g, &Server{})
    // 3. 启动,绑定端口
    lis, err := net.Listen("tcp", "127.0.0.1:8888")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    err = g.Serve(lis)
    if err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}
go复制代码

使用常见

  • 反爬,过滤

使用场景

用户校验

proto

syntax = "proto3";

option go_package = ".;proto";

service Greeter{
  rpc SayHello(HelloRequest) returns (HelloResponse); // Hello 的接口
}


message HelloRequest {
  string name = 1; // 1 是编号,他不是值!
}

message HelloResponse {
  string  message = 1;
}
protobuf复制代码

server

package main

import (
    "context"
    "fmt"
    "go-grpc/grpc_interpretor/proto"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"
    "log"
    "net"
)

type Server_inter struct {
    proto.UnimplementedGreeterServer // 嵌入这个方法,这样我们就不需要手动实现了
}

// 限制死,context可以限制时间

func (s *Server_inter) SayHello(ctx context.Context, req *proto.HelloRequest) (*proto.HelloResponse, error) {
    // 检验 metadata
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        fmt.Println("get metadata failed", ok)
        return &proto.HelloResponse{Message: "校验未通过"}, nil
    } // 校验未通过

    // 校验通过
    for key, val := range md {
        fmt.Println(key, val)
    }

    return &proto.HelloResponse{
        Message: "hello " + req.Name + " " + md.Get("timestamp")[0],
    }, nil
}

func main() {
    // 拦截器函数
    interceptor := func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
        fmt.Println("接受到了一个新的请求")
        md, ok := metadata.FromIncomingContext(ctx)
        // 错误处理
        if !ok {
            // grpc 的错误处理
            fmt.Println("get metadata error")
            return resp, status.Error(codes.Unauthenticated, "无token认证信息")
        }
        var (
            appid  string
            appkey string
        )

        // 赋值操作
        if val, ok := md["appid"]; ok {
            appid = val[0]
        }
        if val, ok := md["appkey"]; ok {
            appkey = val[0]
        }

        if appid != "101010" || appkey != "i am a key" {
            return resp, status.Error(codes.Unauthenticated, "无token认证信息")
        }
        fmt.Println("成功调用!")
        fmt.Printf("appid = %v ;appkey = %v\n", appid, appkey)

        res, err := handler(ctx, req)
        fmt.Println("请求已经完成")
        return res, err
    }

    // 生成拦截器
    opt := grpc.UnaryInterceptor(interceptor) // 返回 ServerOption

    // 1. 实例化一个 grpc 的 server
    g := grpc.NewServer(opt)
    // 2. 注册
    proto.RegisterGreeterServer(g, &Server_inter{})
    // 3. 启动,绑定端口
    lis, err := net.Listen("tcp", "127.0.0.1:8888")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    err = g.Serve(lis)
    if err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}
go复制代码

client

package main

import (
    "context"
    "fmt"
    "go-grpc/grpc_interpretor/proto"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "google.golang.org/grpc/metadata"
    "time"
)

const (
    timestampFormat = time.StampNano
)

// 调用的话 直接 拷贝一份 .proto 文件
// 然后 生成相关的代码  【千万千万不要修改代码!】

func main() {
    // client 端的拦截器
    interceptor := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
        start := time.Now()
        err := invoker(ctx, method, req, reply, cc)
        fmt.Println("耗时---> %s", time.Since(start))
        return err
    }

    DialOption := grpc.WithUnaryInterceptor(interceptor) // 返回 DialOption

    // DialOption的一种写法
    var opts []grpc.DialOption
    opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
    opts = append(opts, DialOption)

    conn, err := grpc.NewClient("127.0.0.1:8888", opts...)
    if err != nil {
        panic(err)
    }
    defer conn.Close()

    c := proto.NewGreeterClient(conn)

    // metadata
    // New 和 Pairs 都可以
    md := metadata.Pairs(
        "timestamp", time.Now().Format(timestampFormat),
        "appid", "101010",
        "appkey", "i am a key")

    //md := metadata.New(map[string]string{
    //	"timestamp": time.Now().Format(timestampFormat),
    //})

    // 会返回一个 context.Background()
    ctx := metadata.NewOutgoingContext(context.Background(), md)

    // 调用
    res, err := c.SayHello(ctx, &proto.HelloRequest{
        Name: "LiShun",
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(res.Message)
}
go复制代码
github