Stream grpc中多个一元rpc调用与长时间运行的双向流?

Stream grpc中多个一元rpc调用与长时间运行的双向流?,stream,grpc,grpc-java,Stream,Grpc,Grpc Java,我有一个用例,在这个用例中,许多客户机需要不断地向服务器发送很多度量(几乎是永久性的)。服务器需要存储这些事件,并在以后处理它们。我不希望服务器对这些事件做出任何响应。 我正在考虑用grpc来做这个。起初,我认为客户端流式处理可以(),但问题是客户端流式处理无法确保应用程序级别的可靠传递(即,如果流在这两个级别之间关闭,那么服务器实际处理了多少消息),而我负担不起这一点。 我的想法是,我应该使用bidi流、服务器流中的ACK或多个一元rpc调用(可能在重复字段中对事件进行批处理以提高性能)。 哪

我有一个用例,在这个用例中,许多客户机需要不断地向服务器发送很多度量(几乎是永久性的)。服务器需要存储这些事件,并在以后处理它们。我不希望服务器对这些事件做出任何响应。
我正在考虑用grpc来做这个。起初,我认为客户端流式处理可以(),但问题是客户端流式处理无法确保应用程序级别的可靠传递(即,如果流在这两个级别之间关闭,那么服务器实际处理了多少消息),而我负担不起这一点。
我的想法是,我应该使用bidi流、服务器流中的ACK或多个一元rpc调用(可能在重复字段中对事件进行批处理以提高性能)。
哪一个更好

问题是客户端流无法确保应用程序级别的可靠交付(即,如果流在两者之间关闭,那么服务器实际处理了多少发送的消息),我负担不起

这意味着您需要一个响应。即使响应只是一个确认,从gRPC的角度来看,它仍然是一个响应

一般的方法应该是“使用一元”,除非足够大的问题可以通过流解决,以克服其复杂性和成本。我对此进行了讨论(视频中有幻灯片和YouTube的链接)

例如,如果您有多个后端,那么每个一元RPC可能被发送到不同的后端。这可能会导致这些不同的后端自身同步的高开销。流式RPC在开始时选择后端,并继续使用相同的后端。因此,流可能会降低后端同步的频率,并允许在服务实现中实现更高的性能。但是,当错误发生时,流式处理会增加复杂性,在这种情况下,它会导致RPC变得长寿命,这对负载平衡来说更为复杂。因此,您需要权衡流式/长寿命RPC增加的复杂性是否为您的应用程序带来了足够大的好处


我们通常不建议使用流式RPC来获得更高的gRPC性能。的确,在流上发送消息比新的一元RPC快,但是改进是固定的,并且具有更高的复杂性。相反,当流式RPC可以提供更高的应用程序(您的代码)性能或更低的应用程序复杂性时,我们建议使用流式RPC。

流式RPC确保消息按发送顺序发送,这意味着如果存在并发消息,将出现某种瓶颈。

谷歌的gRPC团队建议不要在一元数据库上使用数据流来提高性能,但有人认为,理论上,数据流应该具有较低的开销。但事实似乎并非如此

对于数量较少的并发请求,这两种请求的延迟似乎相当但是,对于更高的负载,一元调用的性能要高得多。

没有明显的理由让我们更喜欢流而不是一元数,因为使用流会带来其他问题,如

  • 当我们有并发请求时,延迟很差
  • 应用程序级别的复杂实现
  • 缺乏负载平衡:客户端将与一台服务器连接,而忽略任何新服务器
  • 对网络中断的恢复能力差(即使TCP连接中的小中断也会导致连接失败)

这里有一些基准:

谢谢你的回答,埃里克。我已经看过你的演讲了。这部分原因让我怀疑流媒体对于我的用例来说可能是杀伤力过大,并提出了这个问题。你能详细说明第二点和第三点吗?对于“复杂实现”,我认为我们只需遵循文档中的代码,然后就可以设置流,我们的应用程序可以轻松实现代码来读取接收到的数据。由于“缺乏负载平衡”,我们真的需要每个客户端连接到新的服务器吗?我认为每次客户端都必须与一台服务器建立一个连接来传输数据。当然,@Darius关于第2点-必须在应用程序级别上重新建立错误流(当服务器实例发生故障时)-必须在应用程序级别上映射客户端请求和响应(例如,如果我们同时发送了两个getSalary请求({employeeId:uint32}),并且响应类似于{amount:uint64,currency:currency},那么我们需要将请求的员工id与响应进行映射。-第二点可能会让您在响应中添加额外字段(由于内部执行而更改合同)并添加额外的逻辑来处理此问题。第3点-我们可能对gRPC使用两种类型的负载平衡:服务器端或客户端。这两种情况都适用。来自客户端的多个一元调用将均匀分布在多个gRPC后端(良好的负载平衡)。但一旦与单个后端建立了流式连接,客户端的所有负载都会转移到该特定服务器(负载平衡不好)。顺便说一句,这也使得流式处理在需要事务时适用,但在其他情况下则不适用。