漫谈grpc 3:从实践到原理,带你参透 gRPC (9)

单单会用还不行,我们再深剖一下,看看它是怎么实现的。核心代码如下:

func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor {  n := len(interceptors)  if n > 1 {   lastI := n - 1   return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {    var (     chainHandler grpc.UnaryInvoker     curI         int    )    chainHandler = func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error {     if curI == lastI {      return invoker(currentCtx, currentMethod, currentReq, currentRepl, currentConn, currentOpts...)     }     curI++     err := interceptors[curI](currentCtx, currentMethod, currentReq, currentRepl, currentConn, chainHandler, currentOpts...)     curI--     return err    }    return interceptors[0](ctx, method, req, reply, cc, chainHandler, opts...)   }  }     ... }

漫谈grpc 3:从实践到原理,带你参透 gRPC

当拦截器数量大于 1 时,从 interceptors[1] 开始递归,每一个递归的拦截器 interceptors[i] 会不断地执行,最后才真正的去执行 handler 方法。同时也经常有人会问拦截器的执行顺序是什么,通过这段代码你得出结论了吗?

7. 频繁创建 ClientConn 有什么问题?

这个问题我们可以反向验证一下,假设不公用 ClientConn 看看会怎么样?如下:

func BenchmarkSearch(b *testing.B) {  for i := 0; i < b.N; i++ {   conn, err := GetClientConn()   if err != nil {    b.Errorf("GetClientConn err: %v", err)   }   _, err = Search(context.Background(), conn)   if err != nil {    b.Errorf("Search err: %v", err)   }  } }

漫谈grpc 3:从实践到原理,带你参透 gRPC

输出结果:

    ... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files"     ... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files"     ... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files"     ... connection error: desc = "transport: Error while dialing dial tcp :10001: socket: too many open files" FAIL exit status 1

漫谈grpc 3:从实践到原理,带你参透 gRPC

当你的应用场景是存在高频次同时生成/调用 ClientConn 时,可能会导致系统的文件句柄占用过多。这种情况下你可以变更应用程序生成/调用 ClientConn 的模式,又或是池化它,这块可以参考 grpc-go-pool 项目。

8. 客户端请求失败后会默认重试吗?

会不断地进行重试,直到上下文取消。而重试时间方面采用 backoff 算法作为的重连机制,默认的最大重试时间间隔是 120s。

9. 为什么要用 HTTP/2 作为传输协议?

许多客户端要通过 HTTP 代理来访问网络,gRPC 全部用 HTTP/2 实现,等到代理开始支持 HTTP/2 就能透明转发 gRPC 的数据。不光如此,负责负载均衡、访问控制等等的反向代理都能无缝兼容 gRPC,比起自己设计 wire protocol 的 Thrift,这样做科学不少。@ctiller @滕亦飞

10. 在 Kubernetes 中 gRPC 负载均衡有问题?

gRPC 的 RPC 协议是基于 HTTP/2 标准实现的,HTTP/2 的一大特性就是不需要像 HTTP/1.1 一样,每次发出请求都要重新建立一个新连接,而是会复用原有的连接。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgffdj.html