【发布时间】:2020-03-04 23:42:32
【问题描述】:
我有一项服务以相当高的速率传输消息。
目前它由 akka-tcp 提供服务,每分钟发送 350 万条消息。我决定试一试grpc。 不幸的是,它导致吞吐量小得多:每分钟约 50 万条消息,甚至更少。
能否请您推荐如何优化它?
我的设置
硬件:32 核,24Gb 堆。
grpc 版本:1.25.0
消息格式和端点
消息基本上是一个二进制 blob。 客户端将 100K - 1M 和更多消息流式传输到同一个请求中(异步),服务器不响应任何内容,客户端使用无操作观察器
service MyService {
rpc send (stream MyMessage) returns (stream DummyResponse);
}
message MyMessage {
int64 someField = 1;
bytes payload = 2; //not huge
}
message DummyResponse {
}
问题:
与 akka 实现相比,消息率较低。
我观察到 CPU 使用率很低,所以我怀疑 grpc 调用实际上在内部阻塞,尽管它另有说明。调用onNext() 确实不会立即返回,但桌子上还有GC。
我试图产生更多的发件人来缓解这个问题,但没有得到很大的改进。
我的发现 Grpc 在序列化时实际上为每条消息分配了一个 8KB 字节的缓冲区。查看堆栈跟踪:
java.lang.Thread.State: BLOCKED(在对象监视器上) 在 com.google.common.io.ByteStreams.createBuffer(ByteStreams.java:58) 在 com.google.common.io.ByteStreams.copy(ByteStreams.java:105) 在 io.grpc.internal.MessageFramer.writeToOutputStream(MessageFramer.java:274) 在 io.grpc.internal.MessageFramer.writeKnownLengthUncompressed(MessageFramer.java:230) 在 io.grpc.internal.MessageFramer.writeUncompressed(MessageFramer.java:168) 在 io.grpc.internal.MessageFramer.writePayload(MessageFramer.java:141) 在 io.grpc.internal.AbstractStream.writeMessage(AbstractStream.java:53) 在 io.grpc.internal.ForwardingClientStream.writeMessage(ForwardingClientStream.java:37) 在 io.grpc.internal.DelayedStream.writeMessage(DelayedStream.java:252) 在 io.grpc.internal.ClientCallImpl.sendMessageInternal(ClientCallImpl.java:473) 在 io.grpc.internal.ClientCallImpl.sendMessage(ClientCallImpl.java:457) 在 io.grpc.ForwardingClientCall.sendMessage(ForwardingClientCall.java:37) 在 io.grpc.ForwardingClientCall.sendMessage(ForwardingClientCall.java:37) 在 io.grpc.stub.ClientCalls$CallToStreamObserverAdapter.onNext(ClientCalls.java:346)
任何有关构建高吞吐量 grpc 客户端的最佳实践的帮助表示赞赏。
【问题讨论】:
-
你在使用 Protobuf 吗?仅当 MethodDescriptor.Marshaller.stream() 返回的 InputStream 未实现 Drainable 时,才应采用此代码路径。 Protobuf Marshaller 确实支持 Drainable。如果您使用 Protobuf,是否有可能 ClientInterceptor 正在更改 MethodDescriptor?
-
@EricAnderson 感谢您的回复。我尝试了带有 gradle 的标准 protobuf (com.google.protobuf:protoc:3.10.1, io.grpc:protoc-gen-grpc-java:1.25.0) 和
scalapb。可能这个堆栈跟踪确实是从到 scalapb 生成的代码。我删除了与 scalapb 相关的所有内容,但对性能没有太大帮助。 -
@EricAnderson 我解决了我的问题。作为 grpc 的开发者,Ping 你。我的回答有意义吗?