GRPC: сделать высокопроизводительный клиент на Java / Scala - PullRequest
7 голосов
/ 08 ноября 2019

У меня есть служба, которая передает сообщения с довольно высокой скоростью.

В настоящее время она обслуживается akka-tcp и составляет 3,5 миллиона сообщений в минуту. Я решил попробовать grpc. К сожалению, это привело к гораздо меньшей пропускной способности: ~ 500 тыс. Сообщений в минуту и ​​даже меньше.

Не могли бы вы порекомендовать, как его оптимизировать?

Мои настройки

Аппаратное обеспечение : 32 ядра, куча 24 ГБ.

версия grpc : 1,25.0

Формат сообщения и конечная точка

Сообщение в основном представляет собой двоичный двоичный объект. Клиент передает 100K - 1M и более сообщений в один и тот же запрос (асинхронно), сервер не отвечает ни на что, клиент использует наблюдателя без операций

service MyService {
    rpc send (stream MyMessage) returns (stream DummyResponse);
}

message MyMessage {
    int64 someField = 1;
    bytes payload = 2;  //not huge
}

message DummyResponse {
}

Проблемы: скорость передачи сообщений низкая по сравнению с реализацией akka,Я наблюдаю низкую загрузку ЦП, поэтому я подозреваю, что вызов grpc фактически блокируется внутри, несмотря на то, что он говорит об обратном. Вызов onNext() действительно не сразу возвращается, но на столе также есть GC.

Я пытался породить больше отправителей, чтобы смягчить эту проблему, но не получил большого улучшения.

Мои выводы Grpc фактически выделяет 8-килобайтный буфер байтов для каждого сообщения при его сериализации. Смотрите трассировку стека:

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: 2: наFFio.grpc.internal.MessageFramer.writeUncompressed (MessageFramer.java:168) в io.grpc.internal.MessageFramer.writePayload (MessageFramer.java:141) в io.grpc.internal.AbstractStream.writeMessage (AbstractStream.j::в io.grpc.internal.ForwardingClientStream.writeMessage (ForwardingClientStream.java:37) в io.grpc.internal.DelayedStream.writeMessage (DelayedStream.java:252) в io.grpc.internal.ClientCallImpl.l3 () в 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 оценили.

Ответы [ 3 ]

2 голосов
/ 13 ноября 2019

Я решил проблему, создав несколько экземпляров ManagedChannel для каждого пункта назначения. Несмотря на то, что в статьях говорится, что ManagedChannel может сам порождать достаточно соединений, так что достаточно одного экземпляра, но это не так в моем случае.

Производительность находится в паритете с реализацией akka-tcp.

0 голосов
/ 14 ноября 2019

Я очень впечатлен тем, как хорошо Akka TCP выступил здесь: D

Наш опыт немного отличался. Мы работали над гораздо меньшими экземплярами, используя кластер Akka. Для удаленного взаимодействия Akka мы изменили TCP / Akka на UDP, используя Artery, и получили гораздо более высокую скорость + более низкое и более стабильное время отклика. В Artery есть даже конфигурация, помогающая сбалансировать потребление ЦП и время отклика при холодном запуске.

Я предлагаю использовать некоторую платформу на основе UDP, которая также заботится о надежности передачи (например, Artery UDP). ) и просто сериализовать, используя Protobuf, вместо использования полной плоти gRPC. Канал передачи HTTP / 2 на самом деле не предназначен для целей с высокой пропускной способностью и низким временем отклика.

0 голосов
/ 09 ноября 2019

Интересный вопрос. Компьютерные сетевые пакеты кодируются с использованием стека протоколов , и такие протоколы построены на основе спецификаций предыдущего. Следовательно, производительность (пропускная способность) протокола ограничена производительностью того, который использовался для его построения, поскольку вы добавляете дополнительные шаги кодирования / декодирования поверх базового.

Например, gRPC - этопостроенный поверх HTTP 1.1/2, который является протоколом на прикладном уровне или L7, и поэтому его производительность ограничена производительностью HTTP. Теперь HTTP сам по себе построен на TCP, который находится на Транспортном уровне или L4, поэтому мы можем сделать вывод, что пропускная способность gRPC не может быть больше, чемэквивалентный код, обслуживаемый в слое TCP.

Другими словами: если ваш сервер способен обрабатывать необработанные TCP пакеты, как добавление новых уровней сложности (gRPC) улучшит производительность?

...