Into grpc: unamry and streaminterceptor
Original address:Into grpc: unamry and streaminterceptor
Project address:https://github.com/EDDYCJY/go …
Preface
I want to do something before or after each RPC method, how?
Interceptor, which will be introduced in this chapter, can help you realize these functions in the right place.
There are several ways
In gRPC, the major classes can be divided into two RPC methods. The corresponding relationship with interceptors is:
- Common method: grpc.UnaryInterceptor
- Stream method: stream interceptor (grpc.StreamInterceptor)
Have a look
grpc.UnaryInterceptor
func UnaryInterceptor(i UnaryServerInterceptor) ServerOption {
return func(o *options) {
if o.unaryInt != nil {
panic("The unary server interceptor was already set and may not be reset.")
}
o.unaryInt = i
}
}
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
By looking at the source code, we can know that to complete an interceptor needs to be implementedUnaryServerInterceptor
Methods. The formal parameters are as follows:
- Context: request context
- Reqinterface {}: request parameters for rpc methods
- Info * unaryserInfo: all information for rpc methods
- HandlerUnaryHandler: rpc method itself
grpc.StreamInterceptor
func StreamInterceptor(i StreamServerInterceptor) ServerOption
type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
StreamServerInterceptor and UnaryServerInterceptor parameters have the same meaning and will not be repeated here.
How to Realize Multiple Interceptors
In addition, it can be found that gRPC itself can only set up one interceptor. Can all logic be written together?
On this point, you can rest assured. Adoption of Open Source Projectsgo-grpc-middlewareThis problem can be solved and will be used in this chapter.
import "github.com/grpc-ecosystem/go-grpc-middleware"
myServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
...
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
...
)),
)
gRPC
Starting from this section, we will write the code of gRPC interceptor, and we will implement the following interceptors:
- Logging: Log Output of Input and Output Parameters of RPC Method
- Recover: Exception Protection and Log Output of RPC Method
Implementation of interceptor
logging
func LoggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
log.Printf("gRPC method: %s, %v", info.FullMethod, req)
resp, err := handler(ctx, req)
log.Printf("gRPC method: %s, %v", info.FullMethod, resp)
return resp, err
}
recover
func RecoveryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
defer func() {
if e := recover(); e != nil {
debug.PrintStack()
err = status.Errorf(codes.Internal, "Panic err: %v", e)
}
}()
return handler(ctx, req)
}
Server
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
"log"
"net"
"runtime/debug"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/status"
"google.golang.org/grpc/codes"
"github.com/grpc-ecosystem/go-grpc-middleware"
pb "github.com/EDDYCJY/go-grpc-example/proto"
)
...
func main() {
c, err := GetTLSCredentialsByCA()
if err != nil {
log.Fatalf("GetTLSCredentialsByCA err: %v", err)
}
opts := []grpc.ServerOption{
grpc.Creds(c),
grpc_middleware.WithUnaryServerChain(
RecoveryInterceptor,
LoggingInterceptor,
),
}
server := grpc.NewServer(opts...)
pb.RegisterSearchServiceServer(server, &SearchService{})
lis, err := net.Listen("tcp", ":"+PORT)
if err != nil {
log.Fatalf("net.Listen err: %v", err)
}
server.Serve(lis)
}
Verification
logging
Start simple_server/server.go, execute the request initiated by simple_client/client.go, and get the result:
$ go run server.go
2018/10/02 13:46:35 gRPC method: /proto.SearchService/Search, request:"gRPC"
2018/10/02 13:46:35 gRPC method: /proto.SearchService/Search, response:"gRPC Server"
recover
In RPC method, run-time errors are artificially created, and then server/client.go is repeatedly started, resulting in the following results:
client
$ go run client.go
2018/10/02 13:19:03 client.Search err: rpc error: code = Internal desc = Panic err: assignment to entry in nil map
exit status 1
server
$ go run server.go
goroutine 23 [running]:
runtime/debug.Stack(0xc420223588, 0x1033da9, 0xc420001980)
/usr/local/Cellar/go/1.10.1/libexec/src/runtime/debug/stack.go:24 +0xa7
runtime/debug.PrintStack()
/usr/local/Cellar/go/1.10.1/libexec/src/runtime/debug/stack.go:16 +0x22
main.RecoveryInterceptor.func1(0xc420223a10)
...
Check whether the service is still running, and you will know whether Recovery has taken effect successfully.
Summary
Through this chapter, you can learn the most common methods of using interceptors. Next, other “new” requirements need only be extrapolated
References
This series of sample codes
Series catalog
- Bring gRPC:gRPC and Related Introduction
- Bring in grpc: grpcclient and server
- Bring in grpc: grpcstreaming, clientand server
- Bring in gRPC:TLS certificate authentication
- Bring in gRPC: ca-based TLS certificate authentication
- Into grpc: unamry and streaminterceptor
- Bring in gRPC: Let your service provide HTTP interface at the same time
- Bring into gRPC: Make custom authentication for RPC method
- Bring in grpcs: grpcdeals