思考

3.14 思考

3.14.1 Q&A

3.14.1.1 调用 grpc.Dial 会去连接服务端吗?

会,是异步连接的,连接状态为 Connecting(正在连接)状态。但如果你设置了 grpc.WithBlock 选项,就会阻塞等待(等待到达 Ready 状态)。

另外你需要注意,当未设置 grpc.WithBlock 时,上下文(context)超时控制对其无任何效果。

3.14.1.2 调用 ClientConn 不 Close 会导致泄露吗?

有可能会,除非你的客户端不是常驻进程,那么在应用结束时会被动地回收资源。但如果是常驻进程,你又真的忘记执行 Close 语句,有可能会造成泄露。

3.14.1.3 不控制超时的话,会出现什么问题?

短时间内不会出现问题,但是会不断积蓄泄露,积蓄到最后当然就是服务无法正常运行了。并且在自身服务逐渐出现问题后,更严重的是会影响上下游的服务调用,因此在前面的章节中我们也有提到,因此默认进行上下文的超时控制非常重要。

3.14.1.4 频繁创建 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)
		}
	}
}

输出结果:

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

当你的应用场景是存在高频次同时生成/调用 ClientConn 时,会导致系统的文件句柄占用过多。这种情况下你应该调整你的代码,不要这样子调用。

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

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

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

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

当使用使用 K8s Service 做负载均衡的情况下,将导致 kube-proxy 只有在连接建立时才会做负载均衡,而在这之后的同一个客户端的每一次 RPC 请求都会复用原本的连接,那么实际上后续的每一次的 RPC 请求都跑到了同一个服务端处理,最终导致负载不均。

3.14.1.7 用什么微服务框架?

在完整的微服务框架选型上,业界中有公司内部自研,也有很多外部的开源解决方案。并且我相信接下来会有越来越多的项目释出。大家可以根据公司的实际情况进行选型,不同的方案均有利有弊,没有绝对的合适,因此根据自身企业情况进行选型非常重要,而对于 gRPC 相关技术栈的熟知和了解,更决定着你能够走多远,是否能够及时调整方向。

3.14.2 小结

  • 客户端请求若使用 grpc.Dial 方法默认是异步建立连接,连接状态为 Connecting。
  • 客户端请求若需要同步则调用 WithBlock() 方法,连接状态为 Ready。
  • 特定场景下,如果不对 grpc.ClientConn 加以调控,会导致文件句柄超时系统限制,影响正常使用。
  • 若内部调用完毕后,grpc.ClientConn 不关闭连接,有可能会导致 Goroutine、Memory 等等出现问题。
  • 任何内/外调用如果不加超时控制,会导致泄漏和客户端不断重试,并且最终导致上下游服务出现连环问题,非常危险。
  • 在选择 gRPC 的负载均衡模式时,需要谨慎,确定其在哪一层进行负载,若直接使用 Kubernetes Service 不加以调整,会出现问题,会导致负载不均。
  • 完整的微服务框架需要根据企业实际情况选型,也并非是集成式的就好,也非自研就一定适合你,具体情况具体讨论。


本图书由 煎鱼 ©2020 版权所有,所有文章采用知识署名-非商业性使用-禁止演绎 4.0 国际进行许可。