У меня есть сервис, который передает сообщения с довольно высокой скоростью.
В настоящее время он обслуживается 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 (MessageFramer.java:230) в io.grpc.internal.javaFramerFramer.wmer.Fmer : 168) в io.grpc.internal.MessageFramer.writePayload (MessageFramer.java:141) в io.grpc.internal.AbstractStream.writeMessage (AbstractStream.java:53) в io.grpc.internal.ForwardingClientStream.writeMessageStreaming. Forward. 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 (ForwardingClioCall.Force.IcCent.dll) (ForwardingClientCall.java:37) в io.grpc.stub.ClientCalls $ CallToStreamObserverAdapter.onNext (ClientCalls.java:346)
Любая помощь с лучшими практиками по созданию высокопроизводительных клиентов GRPC приветствуется.
scalapb
. Вероятно, эта трассировка стека действительно была от сгенерированного скальпом кода. Я удалил все, что связано с scalapb, но это не сильно повлияло на производительность.Ответы:
Я решил проблему, создав несколько
ManagedChannel
экземпляров для каждого пункта назначения. Несмотря на статьи говорят, чтоManagedChannel
может порождать достаточно соединений, поэтому достаточно одного экземпляра, но в моем случае это не такПроизводительность находится в паритете с реализацией akka-tcp.
источник
Интересный вопрос. Компьютерные сетевые пакеты кодируются с использованием стека протоколов , и такие протоколы построены на основе спецификаций предыдущего. Следовательно, производительность (пропускная способность) протокола ограничена производительностью того, который использовался для его построения, поскольку вы добавляете дополнительные этапы кодирования / декодирования поверх базового.
Например
gRPC
, построен поверхHTTP 1.1/2
, который является протоколом на уровне приложений , илиL7
, и, как таковая, его производительность ограничена производительностьюHTTP
. ТеперьHTTP
он строится поверхTCP
, на транспортном уровне , илиL4
, таким образом, мы можем сделать вывод, чтоgRPC
пропускная способность не может быть больше, чем эквивалентный код, обслуживаемый наTCP
уровне.Другими словами: если ваш сервер способен обрабатывать необработанные
TCP
пакеты, как добавление новых уровней сложности (gRPC
) повысит производительность?источник
gRPC
вас также заплатить один раз для установления соединения, но вы добавили дополнительное бремя разбора Protobuf. В любом случае, трудно догадываться без лишней информации, но я бы поспорил, что в целом, поскольку вы добавляете дополнительные шаги кодирования / декодирования в свой конвейер,gRPC
реализация будет медленнее, чем аналогичная веб-сокетная.Я очень впечатлен тем, как хорошо Akka TCP выступил здесь: D
Наш опыт был немного другим. Мы работали над гораздо меньшими экземплярами, используя кластер Akka. Для удаленного взаимодействия Akka мы изменили TCP / Akka на UDP, используя Artery, и получили гораздо более высокую скорость + более низкое и более стабильное время отклика. В Artery есть даже конфиг, помогающий сбалансировать потребление ресурсов процессора и время отклика при холодном запуске.
Мое предложение состоит в том, чтобы использовать некоторую платформу на основе UDP, которая также заботится о надежности передачи (например, это Artery UDP), и просто сериализовать, используя Protobuf, вместо использования полной gRPC плоти. Канал передачи HTTP / 2 не предназначен для целей с высокой пропускной способностью и низким временем отклика.
источник