【问题标题】:gRPC slow serialization on large dataset大型数据集上的 gRPC 慢速序列化
【发布时间】:2022-02-09 02:29:06
【问题描述】:

我知道 google 声明 protobufs 不支持大消息 (i.e. greater than 1 MB),但我正在尝试使用数十兆字节的 gRPC 流式传输数据集,似乎有人说它是 ok,或至少与some splitting...

但是,当我尝试以这种方式 (repeated uint32) 发送数组时,在同一台本地计算机上大约需要 20 秒。

#proto
service PAS {
  // analyze single file
  rpc getPhotonRecords (PhotonRecordsRequest) returns (PhotonRecordsReply) {}
}

message PhotonRecordsRequest {
  string fileName = 1;
}

message PhotonRecordsReply {
  repeated uint32 PhotonRecords = 1;
}

PhotonRecordsReply 的长度需要约为 1000 万个 uint32...

有人知道如何加快速度吗?或者什么技术更合适?

所以我认为我已经实现了基于 cmets 和给出的答案的流式传输,但仍然需要相同的时间:

#proto
service PAS {
  // analyze single file
  rpc getPhotonRecords (PhotonRecordsRequest) returns (stream PhotonRecordsReply) {}
}
class PAS_GRPC(pas_pb2_grpc.PASServicer):

    def getPhotonRecords(self, request: pas_pb2.PhotonRecordsRequest, _context):
        raw_data_bytes = flb_tools.read_data_bytes(request.fileName)
        data = flb_tools.reshape_flb_data(raw_data_bytes)
        index = 0
        chunk_size = 1024
        len_data = len(data)
        while index < len_data:
            # last chunk
            if index + chunk_size > len_data:
                yield pas_pb2.PhotonRecordsReply(PhotonRecords=data[index:])
            # all other chunks
            else:
                yield pas_pb2.PhotonRecordsReply(PhotonRecords=data[index:index + chunk_size])
            index += chunk_size

最小重现 Github example

【问题讨论】:

  • 您的示例中的getPhotonRecords RPC 不是流方法。您包含的参考更准确地说“不鼓励使用大于 1MB 的消息”,但流式传输代表“大型数据集”(许多较小的消息),并且使用这种方法是合适的。
  • 发送包含uint32 重复(数组)的消息可能是“具有挑战性的”。 uint32(以及 protos 中的其他整数编码)使用可变长度编码。见:developers.google.com/protocol-buffers/docs/proto#scalar
  • @DazWilkin 是否编辑似乎应该与流媒体一起运行?它仍然很慢......

标签: protocol-buffers grpc proto


【解决方案1】:

如果您将其更改为使用应该有帮助的流。为我传输不到 2 秒。请注意,这是没有 ssl 并且在 localhost 上。我把这段代码放在一起。我确实运行了它并且它有效。例如,如果文件不是 4 字节的倍数,不确定会发生什么。此外,读取字节的 endian 顺序是 Java 的默认值。

我这样制作了 10 兆的文件。

dd if=/dev/random  of=my_10mb_file bs=1024 count=10240

这是服务定义。我在这里添加的唯一内容是响应流。

service PAS {
  // analyze single file
  rpc getPhotonRecords (PhotonRecordsRequest) returns (stream PhotonRecordsReply) {}
}

这是服务器实现。

public class PhotonsServerImpl extends PASImplBase {

  @Override
  public void getPhotonRecords(PhotonRecordsRequest request, StreamObserver<PhotonRecordsReply> responseObserver) {
    log.info("inside getPhotonRecords");
    
    // open the file, I suggest using java.nio API for the fastest read times.
    Path file = Paths.get(request.getFileName());
    try (FileChannel fileChannel = FileChannel.open(file, StandardOpenOption.READ)) {

      int blockSize = 1024 * 4;
      ByteBuffer byteBuffer = ByteBuffer.allocate(blockSize);
      boolean done = false;
      while (!done) {
        PhotonRecordsReply.Builder response = PhotonRecordsReply.newBuilder();
        // read 1000 ints from the file.
        byteBuffer.clear();
        int read = fileChannel.read(byteBuffer);
        if (read < blockSize) {
          done = true;
        }
        // write to the response.
        byteBuffer.flip();
        for (int index = 0; index < read / 4; index++) {
          response.addPhotonRecords(byteBuffer.getInt());
        }
        // send the response
        responseObserver.onNext(response.build());
      }
    } catch (Exception e) {
      log.error("", e);
      responseObserver.onError(
          Status.INTERNAL.withDescription(e.getMessage()).asRuntimeException());
    }
    responseObserver.onCompleted();
    log.info("exit getPhotonRecords");

  }
}

客户端只记录接收到的数组的大小。

public long getPhotonRecords(ManagedChannel channel) {
  if (log.isInfoEnabled())
    log.info("Enter - getPhotonRecords ");

  PASGrpc.PASBlockingStub photonClient = PASGrpc.newBlockingStub(channel);

  PhotonRecordsRequest request = PhotonRecordsRequest.newBuilder().setFileName("/udata/jdrummond/logs/my_10mb_file").build();

  photonClient.getPhotonRecords(request).forEachRemaining(photonRecordsReply -> {
    log.info("got this many photons: {}", photonRecordsReply.getPhotonRecordsCount());
  });

  return 0;
}

【讨论】:

  • 我用我认为你的答案的python版本更新了上面的问题......但它仍然运行缓慢。对我可能的错误有任何见解吗?
  • 也只是跑了一个时间比较,1MB数据流需要5s,而一元需要10s……但还是差别不大,我好像在看其他地方在线人有ms级别的速度! - github.com/grpc/grpc-dotnet/issues/1080
  • 问题:你没有在调试器中运行对吗?这会影响时间。我的电脑可能比你的快?另外,您确实看到了我使用流媒体发送 10 meg 的小块,每块大约 4K,对吧?
  • 我相信使用 Python 运行调试与否没有区别,因为它是 JIT 编译的......我检查了一下,我得到了 1K 块。此外,我拥有的计算机应该很快,而且我认为它不会比我在上面的 github 链接上看到的慢 10-100 倍:\
  • @aerobiotic 我刚刚创建了一个 min repro 示例并添加到原始帖子中
猜你喜欢
  • 2018-08-05
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-30
相关资源
最近更新 更多