gRPC的那些事 - interceptor

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

gRPC-Go 增加了攔截器(interceptor)的功能, 就像Java Servlet中的 filter一樣,可以對RPC的請求和響應進行攔截處理,而且既可以在用戶端進行攔截,也可以對伺服器端進行攔截。

利用攔截器,可以對gRPC進行擴充,利用社區的力量將gRPC發展壯大,也可以讓開發人員更靈活地處理gRPC流程中的商務邏輯。下面列出了利用攔截器實現的一些功能架構:

  1. Go gRPC Middleware:提供了攔截器的interceptor鏈式的功能,可以將多個攔截器組合成一個攔截器鏈,當然它還提供了其它的功能,所以以gRPC中介軟體命名。
  2. grpc-multi-interceptor: 是另一個interceptor鏈式功能的庫,也可以將單向的或者流式的攔截器組合。
  3. grpc_auth: 身分識別驗證攔截器
  4. grpc_ctxtags: 為上下文增加Tag map對象
  5. grpc_zap: 支援zap日誌架構
  6. grpc_logrus: 支援logrus日誌架構
  7. grpc_prometheus: 支援 prometheus
  8. otgrpc: 支援opentracing/zipkin
  9. grpc_opentracing:支援opentracing/zipkin
  10. grpc_retry: 為用戶端增加重試的功能
  11. grpc_validator: 為伺服器端增加校正的功能
  12. xrequestid: 將request id 設定到context中
  13. go-grpc-interceptor: 解析Accept-Language並設定到context
  14. requestdump: 輸出request/response

也有其它一些文章介紹的利用攔截器的例子,如下面的兩篇文章:
Introduction to OAuth on gRPC、gRPC實踐 攔截器 Interceptor

相信會有更多有趣的攔截器被貢獻出來。

注意,伺服器只能配置一個 unary interceptor和 stream interceptor,否則會報錯,用戶端也是,雖然不會報錯,但是只有最後一個才起作用。 如果你想配置多個,可以使用前面提到的攔截器鏈或者自己實現一個。

實現攔截器麻煩嗎?一點都不麻煩,相反,非常的簡單。

對於伺服器端的單向調用的攔截器,只需定義一個UnaryServerInterceptor方法:

1
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)

對於伺服器端stream調用的攔截器,只需定義一個StreamServerInterceptor方法:

1
type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error

方法的參數中包含了上下文,請求和stream以及要調用對象的資訊。

對於用戶端的單向的攔截,只需定義一個``方法:

1
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error

對於用戶端的stream的攔截,只需定義一個``方法:

1
type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)

你可以查看上面提到的一些開源的攔截器的實現,它們的實現都不是太複雜,下面我們以一個簡單的例子來距離,在方法調用的前後列印一個log。

Server端的攔截器

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
package mainimport ("log""net""flag"pb "github.com/smallnest/grpc/a/pb""golang.org/x/net/context""google.golang.org/grpc""google.golang.org/grpc/reflection")var (port = flag.String("p", ":8972", "port"))type server struct{}func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {return &pb.HelloReply{Message: "Hello " + in.Name}, nil}func main() {flag.Parse()lis, err := net.Listen("tcp", *port)if err != nil {log.Fatalf("failed to listen: %v", err)}s := grpc.NewServer(grpc.StreamInterceptor(StreamServerInterceptor),grpc.UnaryInterceptor(UnaryServerInterceptor))pb.RegisterGreeterServer(s, &server{})reflection.Register(s)if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}}func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {log.Printf("before handling. Info: %+v", info)resp, err := handler(ctx, req)log.Printf("after handling. resp: %+v", resp)return resp, err}// StreamServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Streaming RPCs.func StreamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {log.Printf("before handling. Info: %+v", info)err := handler(srv, ss)log.Printf("after handling. err: %v", err)return err}

grpc.NewServer可以將攔截器作為參數傳入,在提供服務的時候,我們可以看到攔截器列印出log:

12
2017/04/17 23:34:20 before handling. Info: &{Server:0x17309c8 FullMethod:/pb.Greeter/SayHello}2017/04/17 23:34:20 after handling. resp: &HelloReply{Message:Hello world,}

用戶端的攔截器

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package mainimport (//"context""flag""log""golang.org/x/net/context"pb "github.com/smallnest/grpc/a/pb""google.golang.org/grpc")var (address = flag.String("addr", "localhost:8972", "address")name    = flag.String("n", "world", "name"))func main() {flag.Parse()// 串連伺服器conn, err := grpc.Dial(*address, grpc.WithInsecure(), grpc.WithUnaryInterceptor(UnaryClientInterceptor),grpc.WithStreamInterceptor(StreamClientInterceptor))if err != nil {log.Fatalf("faild to connect: %v", err)}defer conn.Close()c := pb.NewGreeterClient(conn)r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: *name})if err != nil {log.Fatalf("could not greet: %v", err)}log.Printf("Greeting: %s", r.Message)}func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {log.Printf("before invoker. method: %+v, request:%+v", method, req)err := invoker(ctx, method, req, reply, cc, opts...)log.Printf("after invoker. reply: %+v", reply)return err}func StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {log.Printf("before invoker. method: %+v, StreamDesc:%+v", method, desc)clientStream, err := streamer(ctx, desc, cc, method, opts...)log.Printf("before invoker. method: %+v", method)return clientStream, err}

通過grpc.WithUnaryInterceptorgrpc.WithStreamInterceptor可以將攔截器傳遞給Dial做參數。在用戶端調用的時候,可以查看攔截器輸出的日誌:

123
2017/04/17 23:34:20 before invoker. method: /pb.Greeter/SayHello, request:&HelloRequest{Name:world,}2017/04/17 23:34:20 after invoker. reply: &HelloReply{Message:Hello world,}2017/04/17 23:34:20 Greeting: Hello world

通過這個簡單的例子,你可以很容易的瞭解攔截器的開發。unary和stream兩種類型的攔截器可以根據你的gRPC server/client實現的不同,有選擇的實現。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.