GRPC: Java / Scala'da yüksek verimli istemci oluşturma


9

İletileri oldukça yüksek bir hızla aktaran bir hizmetim var.

Şu anda akka-tcp tarafından servis ediliyor ve dakikada 3,5 milyon mesaj yapıyor. Ben grpc denemeye karar verdim. Ne yazık ki çok daha küçük bir verim ile sonuçlandı: dakikada 500k mesaj daha da az.

Nasıl optimize edeceğinizi tavsiye eder misiniz?

Kurulumum

Donanım : 32 çekirdek, 24 Gb yığın.

grpc sürümü : 1.25.0

Mesaj biçimi ve bitiş noktası

Mesaj temelde ikili bir damladır. İstemci 100K - 1M ve daha fazla iletiyi aynı istekte (eşzamansız olarak) akışa gönderir, sunucu hiçbir şeye yanıt vermez, istemci bir op-olmayan gözlemci kullanır

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

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

message DummyResponse {
}

Sorunlar: Mesaj oranı akka uygulamasına göre düşük. Düşük CPU kullanımını gözlemliyorum, bu yüzden grpc çağrısının aslında aksi belirtilmese de dahili olarak engellendiğinden şüpheleniyorum. onNext()Gerçekten arama hemen geri dönmez, ancak masada GC de vardır.

Bu sorunu hafifletmek için daha fazla gönderici üretmeye çalıştım, ancak çok fazla gelişme elde etmedim.

Bulgularım Grpc, dizileştirirken her iletiye 8KB baytlık bir arabellek ayırıyor. Yığın izine bakın:

java.lang.Thread.State: com.google.common.io.ByteStreams.createBuffer (ByteStreams.java:58) adresinde com.google.common.io.ByteStreams.copy (ByteStreams.java: adresinde BLOCKED (nesne monitöründe): 105) io.grpc.internal.MessageFramer.writeKnownLengthUncompressed'de (MessageFramer.java:274) io.grpc.internal.MessageFramer.writeKnownLengthUncompressed (MessageFramer.java:230) 'da : 168) io.grpc.internal.ForwardingClientStream.writeMessage (ForwardClientStream.writeMessage (ForwardingClientStream.writeMessage) 'da java: 37) io.grpc.internal.DelayedStream.writeMessage'da (DelayedStream.java:252) io.grpc.internal adresinde.İo.grpc.internal.ClientCallImpl.sendMessage (ClientCallImpl.java:457) adresindeki io.grpc.ForwardingClientCall.sendMessage (ClientCallImpl.java:457) adresinde ClientCallImpl.sendMessageInternal (ClientCallImpl.java:473) (ForwardingClientCall.java:37), io.grpc.stub.ClientCalls $ CallToStreamObserverAdapter.onNext (ClientCalls.java:346)

Yüksek verimli grpc istemcileri oluşturma konusunda en iyi uygulamalarla ilgili herhangi bir yardım takdir edildi.


Protobuf mu kullanıyorsunuz? Bu kod yolu yalnızca MethodDescriptor.Marshaller.stream () tarafından döndürülen InputStream, Drenaj uygulamazsa kullanılmalıdır. Protobuf Marshaller, Drainable'ı destekliyor. Protobuf kullanıyorsanız, bir ClientInterceptor yöntemi MethodDescriptor değiştiriyor olabilir mi?
Eric Anderson

@EricAnderson yanıtınız için teşekkür ederim. Standart protobufu gradle (com.google.protobuf: protoc: 3.10.1, io.grpc: protoc-gen-grpc-java: 1.25.0) ve ayrıca denedim scalapb. Muhtemelen bu yığın izlemesi gerçekten de skatb-oluşturulan koddan kaynaklanıyordu. Ben scalapb ile ilgili her şeyi kaldırdım ama çok wrt performansına yardımcı olmadı.
simpadjo

@EricAnderson Sorunumu çözdüm. Size bir grpc geliştiricisi olarak ping atıyoruz. Cevabım anlamlı mı?
simpadjo

Yanıtlar:


4

ManagedChannelHer hedef için birkaç örnek oluşturarak sorunu çözdüm. Makalelere rağmen, bir ManagedChanneltenekenin yeterli bağlantıyı üretebileceğini söyler, bu yüzden bir örnek yeterli, benim durumumda doğru değildi.

Akka-tcp uygulaması ile performans eşittir.


1
ManagedChannel (yerleşik LB ilkeleriyle birlikte) arka uç başına birden fazla bağlantı kullanmaz. Bu nedenle, az sayıda arka uç ile yüksek iş hacminiz varsa, tüm arka uçlarla bağlantıları doyurmak mümkündür. Birden fazla kanal kullanmak bu durumlarda performansı artırabilir.
Eric Anderson

@EricAnderson teşekkürler. Benim durumumda bile tek bir arka uç düğüm için birkaç kanal yumurtlama yardımcı oldu
simpadjo

Arka uçlar ne kadar az ve bant genişliği ne kadar yüksek olursa, birden fazla kanala ihtiyacınız vardır. Bu yüzden "tek arka uç" daha fazla kanalın yardımcı olma olasılığını artırır.
Eric Anderson

0

İlginç soru. Bilgisayar ağ paketleri bir protokol yığını kullanılarak kodlanır ve bu protokoller bir öncekinin özelliklerinin üzerine inşa edilir. Bu nedenle, bir protokolün performansı (verim), onu oluşturmak için kullanılan protokolün performansı ile sınırlıdır, çünkü altta yatan protokolün üstüne ekstra kodlama / kod çözme adımları eklersiniz.

Örneğin gRPCüstüne yerleşik HTTP 1.1/2bir protokol olan uygulama tabakası ya da L7bu tür performans performansı bağlı olarak, ve HTTP. Şimdi HTTPkendisi üstünde inşa olduğu TCPaltındadır, Taşıma katmanının veya L4bunu anlamak böylece, gRPCüretilen iş olamaz servis eşdeğer bir kod daha büyük TCPkatmanın.

Başka bir deyişle: sunucunuz ham TCPpaketleri işleyebiliyorsa , yeni karmaşıklık katmanları ( gRPC) eklemek performansı nasıl artırabilir?


Tam da bu nedenle akış yaklaşımını kullanıyorum: Bir http bağlantısı kurmak ve bunu kullanarak ~ 300M mesajları göndermek için bir kez ödeme yaparım. Başlığın altında nispeten düşük olmasını beklediğim websockets kullanır.
simpadjo

İçin gRPCsize ayrıca bir bağlantı kurmak için bir kez ödeme, ancak Protobuf ayrıştırma ekstra yük ekledik. Her neyse, çok fazla bilgi olmadan tahmin yapmak zordur, ancak genel olarak, boru hattınıza ekstra kodlama / kod çözme adımları eklediğinizden, gRPCuygulamanın eşdeğer web soketinden daha yavaş olacağına bahse girerim .
Batato

Akka da genel gider ekler. Her neyse, x5 yavaşlaması çok fazla görünüyor.
simpadjo

Bunu ilginç bulabileceğinizi düşünüyorum: github.com/REASY/akka-http-vs-akka-grpc , onun durumunda (ve bunun size ait olduğunu düşünüyorum), darboğaz protobuf'ta yüksek bellek kullanımına bağlı olabilir (de ) serileştirme, bu da çöp toplayıcısına daha fazla çağrı tetikler.
Batato

teşekkürler, ilginç zaten sorunumu çözdü rağmen
simpadjo

0

Akka TCP'nin burada ne kadar iyi performans gösterdiğinden oldukça etkilendim: D

Deneyimlerimiz biraz farklıydı. Akka Cluster kullanarak çok daha küçük örnekler üzerinde çalışıyorduk. Akka uzaktan kumandası için Arter kullanarak Akka TCP'den UDP'ye geçtik ve çok daha yüksek bir oran + daha düşük ve daha kararlı yanıt süresi elde ettik. Artery'de CPU tüketimi ile soğuk bir başlangıçtan itibaren tepki süresi arasında denge kurmaya yardımcı olan bir yapılandırma bile vardır.

Benim önerim, sizin için iletim güvenilirliğini de dikkate alan bazı UDP tabanlı bir çerçeve kullanmak (örn. Artery UDP) ve tam et gRPC kullanmak yerine Protobuf kullanarak serileştirmektir. HTTP / 2 iletim kanalı gerçekten yüksek verim düşük yanıt süresi amaçları için değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.