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;
}
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)
}
使用场景:
- 请求失败的话再次重试
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)
}
}
使用常见
- 反爬,过滤
使用场景
用户校验
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;
}
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)
}
}
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)
}