在 GRPC 上打开通道的最佳实践是什么?

hev*_*evi 5 grpc grpc-java

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

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

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

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

ManagedChannel managedChannel = ManagedChannelBuilder
                    .forAddress(host, port)
                    .usePlaintext()
                    .build()
ServiceA.newBlockingStub(managedChannel)).fooBar(...)
Run Code Online (Sandbox Code Playgroud)

Eri*_*son 4

ManagedChannel 应该很少创建并大量重用。当不再使用 ManagedChannel 时,必须将其关闭。否则会泄漏。

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

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

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

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

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

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

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