在 GRPC 上打开频道的最佳做法是什么?

What is the best practice for opening channels on GRPC?

有服务通过 GRpc (100+ req/sec) 调用服务 A(10 个副本),java 生成存根。我们没有负载平衡器,但我很好奇这两种情况下的最佳实践是什么。

客户端应该在每次调用服务时构建通道 A 还是我应该创建一次 managedChannel 直到应用程序关闭?

  1. 如果我为每个请求创建一个,调用会分配到 10 个副本,但如果我只在应用程序启动时创建,所有调用都会转到同一个服务 A 副本。

  2. 另一方面,如果我在每次调用时创建,是否会有数千个连接打开直到它们空闲(默认情况下为 30 分钟)?

ManagedChannel managedChannel = ManagedChannelBuilder
                    .forAddress(host, port)
                    .usePlaintext()
                    .build()
ServiceA.newBlockingStub(managedChannel)).fooBar(...)

ManagedChannels 应该很少创建并大量重复使用。当不再使用 ManagedChannel 时,关闭它是必不可少的。不然会漏。

这是一个负载均衡问题,答案取决于您的负载均衡架构。根据您的描述,您可能使用以下两种结构之一:

  1. 所有后端都暴露在 DNS 中。客户端直接连接到后端。我称之为“暴露”

  2. 有一个 TCP 负载平衡器,客户端创建连接到该负载平衡器,平衡器将该连接扩展到后端。我称之为“隐藏”

对于这两种方法,设置后端 nettyServerBuilder.maxConnectionAge(...) 通常是客户端开始使用新后端所必需的。

在暴露的架构中,您只需在ManagedChannel 中配置负载均衡。这可能与使用 managedChannelBuilder.defaultLoadBalancingPolicy("round_robin") 一样简单。 round_robin 策略将连接到 DNS 返回的每个 IP 地址,并在这些地址之间分发 RPC。当后端因 maxConnectionAge 而断开连接时,客户端将重新解析 DNS 并建立新连接。

在隐藏架构中,如果您有许多客户端,其中每个客户端与每个后端相比都“小”,那么 maxConnectionAge 就足够了。当后端因 maxConnectionAge 而断开连接时,客户端将与负载均衡器建立新连接,负载均衡器可以选择新的后端。

在隐藏架构中,如果您有“大”客户端产生的负载超过单个后端可以处理的负载,那么事情就更难了;客户端无法看到后端的数量及其状态,但后端无法自行管理负载。这里最简单的事情是创建多个通道并在它们之上循环。在 Java 中,您可以将其实现为 Channel,因此它对您的大部分代码都是隐藏的。当后端由于 maxConnectionAge 而断开连接时,一个通道将与负载均衡器建立新连接,负载均衡器可以选择新的后端。这种方法的难点在于知道要创建多少个通道。具有较大客户端的隐藏架构从使用 HTTP 负载平衡而不是 TCP 负载平衡中受益匪浅。即使使用 HTTP 负载平衡,也可能需要使用多个托管通道,以平衡 负载平衡器的负载