Java gRPC server for long-lived streams 有效实现

Java gRPC server for long-lived streams effective implementation

我想了解用于长期流资源管理的 gRPC 框架的一部分。 假设我们有无限的稀有事件源(大约每秒一次),我们希望通过 grpc 流将其流式传输给客户端。 这些事件由服务器上的单个应用程序线程生成。

我看到流式传输事件的两种可能实现方式:

  1. 在 rpc 调用中自旋调用者线程并通过(阻塞)队列与源通信
  2. 将 StreamObserver 暴露给事件生成线程并从那里填充所有客户端流。

选项一似乎很简单,但线程数有点重 - 对于稀疏流,每个客户端一个线程似乎有点矫枉过正。每个线程占用一些heap占用scheduler等等。

选项二看起来对资源更友好一些。但是我在互联网上找不到任何材料来支持这种方法。我不确定 gRPC 服务器不会意外关闭 ServerCall 或 Context 导致流突然关闭。或者可能还有其他一些我不知道的副作用。

所以我的问题是: 实施长寿命流的推荐方法是什么? 是否有任何其他可能的方法来实现所描述的问题。 选项 2 是合法的还是应该坚持使用 1 个客户端 1 个线程方法?

我尝试使用选项二创建原型,它似乎可以正常工作。 但我还是很想知道答案。

从 gRPC 的角度来看,这两种方法都很好。在方便的时候,您可以自由地坚持使用 1 个客户端、1 个线程的方法。对于流式传输情况,通常最好避免在调用者线程中自旋,但您可以使用第二个线程来发送;这很正常。另一方面,将 StreamObserver 传递给单个线程进行管理具有资源优势,也是一种很好的方法。

当事件生成速度快于发送速度时(即流量控制),您应该考虑如何响应慢速客户端。

您需要将提供的 StreamObserver 转换为 ServerCallStreamObserver 以访问其他 API。它提供 setOnReadyHandler(Runnable) and isReady() 用于检测慢速客户端。 gRPC Java 允许您调用 onNext(...) 即使没有准备好,但这样做会缓冲。

on-ready 处理程序是一个回调,它使用与调用者线程相同的线程,因此如果您在调用者线程中旋转,您将无法接收到该回调。这就是为什么对于流式传输,通常最好避免在调用者线程中旋转。

使用专用发送线程的优点是队列清晰,生产者和消费者分离。您可以选择队列大小并决定当该队列已满时要执行的操作。在一个线程中直接与 StreamObserver 交互具有较少的资源使用。两种选择的复杂性各不相同。 “正确”的方法选择基于规模、资源考虑、您的服务细节和您的偏好。