问题 gRPC客户端负载均衡


我正在使用gRPC和Python作为kubernetes pods中的客户端/服务器...... 我希望能够启动多个相同类型的pod(gRPC服务器)并让客户端(随机)连接它们。

我发送了10个服务器的pod并设置了一个“服务”来定位它们。然后,在客户端,我连接到服务的DNS名称 - 这意味着kubernetes应该进行负载平衡并将我引导到随机服务器pod。 实际上,客户端调用gRPC函数(效果很好)但是当我查看日志时,我看到所有调用都转到同一个服务器pod。

我假设客户端正在进行某种DNS缓存,这会导致所有呼叫被发送到同一服务器。是这样的吗?无论如何要禁用它并设置相同的存根客户端进行“新”呼叫并通过DNS每次呼叫获取新的IP?

我知道如果它每次都会查询DNS服务器,我可能会产生的开销,但是分配负载对我来说更重要。

编辑

可能不是缓存问题......可能只是gRPC的工作方式。 HTTP / 2和持久的可重用连接。每次通话后有什么方法可以“断开”?


2946
2017-09-22 16:03


起源



答案:


让我借此机会通过描述事情应如何运作来回答。

客户端LB在gRPC C核心(除Java和Go风格或gRPC之外的所有基础)的工作方式如下(可以找到权威文档) 这里):

客户端LB保持简单,故意“哑”。我们选择实施复杂LB策略的方式是通过外部LB服务器(如上述文档中所述)。你不关心这种情况。相反,你只是创建一个通道,它将使用(默认) 挑一 LB政策。

LB策略的输入是已解析地址的列表。使用DNS时,如果foo.com解析为 [10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.4],该政策将尝试与所有这些政策建立联系。成功连接的第一个将成为所选择的 直到断开连接。因此这个名字“先挑”。一个较长的名字可能是“先选择并尽可能长时间地坚持使用”,但这样做的文件名很长:)。如果/当被拣选的一个断开时,拣选优先策略将移动以返回下一个成功连接的地址(内部称为“连接的子信道”),如果有的话。只要它保持连接,它将再次继续选择这个连接的子通道。如果所有这些都失败,则呼叫将失败。

这里的问题是,基于内在拉取的DNS解析仅在信道创建时触发1)和2)在所选择的连接子信道断开时触发。

到目前为止,一个hacky解决方案是为每个请求创建一个新的通道(效率非常低,但是根据你的设置,它会做到这一点)。

鉴于2017年第一季度的变化(见 https://github.com/grpc/grpc/issues/7818)将允许客户选择不同的LB策略,即Round Robin。此外,我们可能会考虑向该客户端配置引入一个“randomize”位,这会在对其进行Round-Robin之前对地址进行洗牌,从而有效地实现您的意图。


12
2017-09-28 19:37



谢谢你的详细解答。实际上,我已经完成了你的建议并为每个请求创建了一个新的频道(我知道效率不高)。根据你的回答,我明白只有dns中的第一个ip才能获得请求,直到它停止(没有可用的连接/被杀/崩溃),然后只有客户端才会到达第二个ip,等等......是吗? - Idan
是。如上所述,在更改之前可以选择循环代替首选作为LB策略。 - David García Quintas
扩展多个gRPC服务器有什么典型的解决方案吗?或客户端负载平衡? - Ryan Chou


答案:


让我借此机会通过描述事情应如何运作来回答。

客户端LB在gRPC C核心(除Java和Go风格或gRPC之外的所有基础)的工作方式如下(可以找到权威文档) 这里):

客户端LB保持简单,故意“哑”。我们选择实施复杂LB策略的方式是通过外部LB服务器(如上述文档中所述)。你不关心这种情况。相反,你只是创建一个通道,它将使用(默认) 挑一 LB政策。

LB策略的输入是已解析地址的列表。使用DNS时,如果foo.com解析为 [10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.4],该政策将尝试与所有这些政策建立联系。成功连接的第一个将成为所选择的 直到断开连接。因此这个名字“先挑”。一个较长的名字可能是“先选择并尽可能长时间地坚持使用”,但这样做的文件名很长:)。如果/当被拣选的一个断开时,拣选优先策略将移动以返回下一个成功连接的地址(内部称为“连接的子信道”),如果有的话。只要它保持连接,它将再次继续选择这个连接的子通道。如果所有这些都失败,则呼叫将失败。

这里的问题是,基于内在拉取的DNS解析仅在信道创建时触发1)和2)在所选择的连接子信道断开时触发。

到目前为止,一个hacky解决方案是为每个请求创建一个新的通道(效率非常低,但是根据你的设置,它会做到这一点)。

鉴于2017年第一季度的变化(见 https://github.com/grpc/grpc/issues/7818)将允许客户选择不同的LB策略,即Round Robin。此外,我们可能会考虑向该客户端配置引入一个“randomize”位,这会在对其进行Round-Robin之前对地址进行洗牌,从而有效地实现您的意图。


12
2017-09-28 19:37



谢谢你的详细解答。实际上,我已经完成了你的建议并为每个请求创建了一个新的频道(我知道效率不高)。根据你的回答,我明白只有dns中的第一个ip才能获得请求,直到它停止(没有可用的连接/被杀/崩溃),然后只有客户端才会到达第二个ip,等等......是吗? - Idan
是。如上所述,在更改之前可以选择循环代替首选作为LB策略。 - David García Quintas
扩展多个gRPC服务器有什么典型的解决方案吗?或客户端负载平衡? - Ryan Chou


如果您已经创建了一个vanilla Kubernetes服务,该服务应该有自己的负载平衡虚拟IP(检查是否 kubectl get svc your-service 显示一个 CLUSTER-IP 为您服务)。如果是这种情况,DNS缓存应该不是问题,因为单个虚拟IP应该在实际后端之间拆分流量。

尝试 kubectl get endpoints your-service 确认您的服务实际上知道您的所有后端。

如果你有 无头服务,DNS查找将返回一个包含10个IP的A记录(每个Pod一个)。如果您的客户总是选择A记录中的第一个IP,那么这也可以解释您所看到的行为。


1
2017-09-22 17:06



服务器有一个CLUSTER_IP,当我“获取端点”时,我可以看到我拥有所有端点。仍然去同一台服务器。我认为这可能是gRPC工作的方式和连接重用它的HTTP / 2 ... - Idan