1.介绍
GRPC
中的截取器,类似中间件(middleware
)的功能,可以做一些前置校验的工作,比如登陆验证、日志记录、异常捕获等。
2. 流程梳理
gRPC
中的grpc.UnaryInterceptor
和grpc.StreamInterceptor
分别对普通方法和流方法提供了截取器的支持。这里主要编写普通方法的截取器。
- 步骤一: 编写
proto
文件。
- 步骤二: 生成
Go
代码。
- 步骤三:编写服务端代码,并为
grpc.UnaryInterceptor
的参数实现一个函数。
- 步骤五:编写客户端代码。
- 步骤六: 运行测试。
2.1 编写proto
syntax = "proto3"; option go_package = "server/inceptservice";
message Param { string query = 1; }
message Result { int32 code = 1; string msg = 2; }
service InterceptService { rpc Test(Param) returns (Result); }
|
2.2 生成Go
代码
$ protoc --go_out=. --go-grpc_out=. proto/interceptor.proto
|
2.3 编写服务端
package main
import ( "52lu/go-rpc/server/inceptservice" "context" "fmt" "google.golang.org/grpc" "net" "time" )
func filter(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { fmt.Println("time:", time.Now().Unix()) return handler(ctx, req) }
func main() { grpcserver := grpc.NewServer(grpc.UnaryInterceptor(filter)) inceptservice.RegisterInterceptServiceServer(grpcserver, new(inceptservice.UnimplementedInterceptServiceServer)) conn, err := net.Listen("tcp", ":1234") if err != nil { fmt.Println("net.Listen error ", err) return } fmt.Println("启动服务...") grpcserver.Serve(conn) }
|
上述代码中,在grpc.NewServer
中传入为grpc.UnaryInterceptor
实现的一个参数函数(filter
),函数的参数介绍:
ctx
和req
就是每个普通的RPC
方法的前两个参数。
info
表示当前对应的那个gRPC
方法.
handler
对应当前的gRPC
函数。上面的函数中首先是打印时间,然后调用handler
对应的gRPC
函数。
2.4 编写客户端
package main
import ( "52lu/go-rpc/server/inceptservice" "context" "fmt" "google.golang.org/grpc" )
func main() { conn, err := grpc.Dial(":1234", grpc.WithInsecure()) if err != nil { fmt.Println("Dial err ", err) return } defer conn.Close() client := inceptservice.NewInterceptServiceClient(conn) p := inceptservice.Param{ Query: "name=xiaoming", } test, err := client.Test(context.TODO(), &p) if err != nil { fmt.Println("call test err ", err) return } fmt.Println(test.String()) }
|
2.5 运行 & 测试
$ go-rpc go run cmd/intercept/server.go 启动服务...
$ go run cmd/intercept/client.go code:200 msg:"name=xiaoming"
time: 1647919974
|
3.开源截取器
gRPC
框架中只能为每个服务设置一个截取器,因此所有的截取工作只能在一个函数中完成。开源的grpc-ecosystem
项目中的go-grpc-middleware
包已经基于gRPC
对截取器实现了链式截取的支持。
3.1 下载
$ go get github.com/grpc-ecosystem/go-grpc-middleware
|
3.2 使用
package main
import ( "52lu/go-rpc/server/inceptservice" "context" "fmt" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "google.golang.org/grpc" "net" )
func filter(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { fmt.Println("我是第一个过滤器") return handler(ctx, req) } func filte2(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { fmt.Println("我是第二个过滤器") return handler(ctx, req) }
func main() { grpcserver := grpc.NewServer(grpc.UnaryInterceptor( grpc_middleware.ChainUnaryServer(filter, filte2) )) inceptservice.RegisterInterceptServiceServer(grpcserver, new(inceptservice.UnimplementedInterceptServiceServer)) conn, err := net.Listen("tcp", ":1234") if err != nil { fmt.Println("net.Listen error ", err) return } fmt.Println("启动服务...") grpcserver.Serve(conn) }
|
更多使用方法:https://github.com/grpc-ecosystem/go-grpc-middleware