GRPC: สร้างไคลเอนต์ความเร็วสูงใน Java / Scala


9

ฉันมีบริการที่โอนข้อความในอัตราที่ค่อนข้างสูง

ขณะนี้มีให้บริการโดย akka-tcp และสร้างข้อความ 3.5M ต่อนาที ฉันตัดสินใจที่จะลอง grpc โชคไม่ดีที่มันส่งผลให้ปริมาณงานน้อยลง: ~ 500k ข้อความต่อนาทียิ่งน้อยลง

คุณช่วยแนะนำวิธีเพิ่มประสิทธิภาพได้ไหม

การตั้งค่าของฉัน

ฮาร์ดแวร์ : 32 แกน 24Gb กอง

รุ่น grpc: 1.25.0

รูปแบบข้อความและจุดสิ้นสุด

ข้อความนั้นเป็นฐานสองหยด สตรีม 100K - 1M และข้อความอื่น ๆ อีกมากมายในคำขอเดียวกัน (แบบอะซิงโครนัส) เซิร์ฟเวอร์ไม่ตอบสนองกับสิ่งใดลูกค้าใช้ผู้สังเกตการณ์แบบไม่มี op

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 จริงจัดสรรบัฟเฟอร์ 8KB ไบต์ในแต่ละข้อความเมื่อซีเรียลไลซ์ ดูสแต็คเทรซ:

java.lang.Thread.State: บล็อก (บนการตรวจสอบวัตถุ) ที่ 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.writeToOutputStream (MessageFramer.java:274) : 168) ที่ io.grpc.internal.MessageFramer.writePayload (MessageFramer.java:141) ที่ io.grpc.internal.AbstractStream.writeMessage (AbstractStream.java:53) ที่ io.grpc.internal.ForwardingClientStream.writeMessage (ไปข้างหน้า) java: 37) ที่ io.grpc.internal.DelayedStream.writeMessage (DelayedStream.java:252) ที่ io.grpc.internalClientCallImpl.sendMessageInternal (ClientCallImpl.java:473) ที่ io.grpc.internal.ClientCallImpl.sendMessage.CallImpl.sendMessage (ClientCallImpl.java:457) ที่ io.grpc.ForwardingClientCall.sallMessageInterage (ForwardingClientCall.java:37) ที่ io.grpc.stub.ClientCalls $ CallToStreamObserverAdapter.onNext (ClientCalls.java:346)

ความช่วยเหลือใด ๆ กับแนวทางปฏิบัติที่ดีที่สุดในการสร้างลูกค้า grpc ทรูพุตความเร็วสูงชื่นชม


คุณใช้ Protobuf หรือไม่? ควรใช้พา ธ ของรหัสนี้ก็ต่อเมื่อ InputStream ที่ส่งคืนโดย MethodDescriptor.Marshaller.stream () ไม่สามารถนำไปใช้กับ Drainable ได้ Protobuf Marshaller สนับสนุนการระบายได้ หากคุณใช้ Protobuf เป็นไปได้ว่า ClientInterceptor กำลังเปลี่ยน MethodDescriptor หรือไม่
Eric Anderson

@EricAnderson ขอขอบคุณสำหรับการตอบสนองของคุณ ฉันลอง protobuf มาตรฐานที่มี gradle (com.google.protobuf: protoc: 3.10.1, io.grpc: protoc-gen-grpc-java: 1.25.0) และscalapbด้วย อาจสแต็คสแตร็คนี้มาจากรหัสที่สร้างโดย scalapb ฉันลบทุกอย่างที่เกี่ยวข้องกับ scalapb แต่มันไม่ได้ช่วยประสิทธิภาพการทำงานของ wrt มากนัก
simpadjo

@EricAnderson ฉันแก้ไขปัญหาของฉันแล้ว ส่ง Ping ให้คุณในฐานะนักพัฒนา grpc คำตอบของฉันเหมาะสมหรือไม่
simpadjo

คำตอบ:


4

ฉันแก้ไขปัญหาด้วยการสร้างหลายManagedChannelครั้งต่อปลายทาง แม้จะมีบทความที่บอกว่า a ManagedChannelสามารถวางไข่การเชื่อมต่อได้มากพอดังนั้นตัวอย่างหนึ่งก็เพียงพอแล้วมันก็ไม่จริงในกรณีของฉัน

ประสิทธิภาพมีความเท่าเทียมกันกับการใช้ akka-tcp


1
ManagedChannel (ที่มีนโยบาย LB ในตัว) ไม่ได้ใช้การเชื่อมต่อมากกว่าหนึ่งรายการต่อแบ็กเอนด์ ดังนั้นหากคุณมีปริมาณงานสูงโดยมีแบ็กเอนด์น้อยจึงเป็นไปได้ที่จะเชื่อมต่อกับแบ็กเอนด์ทั้งหมด การใช้หลายช่องทางสามารถเพิ่มประสิทธิภาพในกรณีเหล่านั้น
Eric Anderson

@EricAnderson ขอบคุณ ในกรณีของฉันวางไข่หลายช่องทางแม้กระทั่งโหนดแบ็กเอนด์เดียวก็ช่วยได้
simpadjo

ยิ่งแบ็กเอนด์น้อยลงและยิ่งมีแบนด์วิดท์สูงเท่าไหร่คุณก็จะมีช่องทางหลากหลายมากขึ้นเท่านั้น ดังนั้น "แบ็กเอนด์เดียว" จะทำให้ช่องทางมีโอกาสมากขึ้นที่เป็นประโยชน์
Eric Anderson

0

คำถามที่น่าสนใจ แพคเกจเครือข่ายคอมพิวเตอร์มีการเข้ารหัสโดยใช้สแต็คของโปรโตคอลและโปรโตคอลดังกล่าวถูกสร้างขึ้นที่ด้านบนของข้อกำหนดของก่อนหน้านี้ ดังนั้นประสิทธิภาพ (ปริมาณงาน) ของโปรโตคอลจึงถูก จำกัด โดยประสิทธิภาพของโพรโทคอลที่ใช้สร้างมันเนื่องจากคุณกำลังเพิ่มขั้นตอนการเข้ารหัส / ถอดรหัสเพิ่มเติมที่ด้านบนของโปรโตคอล

ยกตัวอย่างเช่นgRPCมีการสร้างขึ้นบนHTTP 1.1/2ซึ่งเป็นโปรโตคอลในชั้นแอพลิเคชันหรือและเป็นเช่นประสิทธิภาพการทำงานที่ถูกผูกไว้โดยการปฏิบัติงานของL7 HTTPตอนนี้HTTPตัวมันเองจะถูกสร้างขึ้นบนTCPซึ่งอยู่ที่Transport layerหรือL4ดังนั้นเราจึงสามารถอนุมานได้ว่าgRPCปริมาณงานไม่สามารถมีขนาดใหญ่กว่ารหัสเทียบเท่าที่แสดงในTCPเลเยอร์

กล่าวอีกนัยหนึ่ง: ถ้าเซิร์ฟเวอร์ของคุณสามารถจัดการTCPแพ็คเกจแบบดิบได้การเพิ่มเลเยอร์ใหม่ของความซับซ้อน ( gRPC) จะปรับปรุงประสิทธิภาพได้อย่างไร


ด้วยเหตุผลนั้นฉันจึงใช้วิธีสตรีมมิ่ง: ฉันจ่ายครั้งเดียวในการสร้างการเชื่อมต่อ http และส่งข้อความ ~ 300M โดยใช้มัน ใช้ websockets ภายใต้ประทุนซึ่งฉันคาดว่าจะมีค่าใช้จ่ายค่อนข้างต่ำ
simpadjo

สำหรับgRPCคุณยังจ่ายเพียงครั้งเดียวสำหรับการสร้างการเชื่อมต่อ แต่คุณได้เพิ่มภาระพิเศษในการแยกโพรโทฟ อย่างไรก็ตามมันเป็นการยากที่จะคาดเดาโดยไม่มีข้อมูลมากเกินไป แต่โดยทั่วไปแล้วฉันเดิมพันว่าโดยทั่วไปแล้วเนื่องจากคุณกำลังเพิ่มขั้นตอนการเข้ารหัส / ถอดรหัสเพิ่มเติมในขั้นตอนการgRPCติดตั้งของคุณ
Batato

Akka จะเพิ่มค่าใช้จ่ายบางอย่างเช่นกัน อย่างไรก็ตามการชะลอตัวของ x5 ดูมากเกินไป
simpadjo

ฉันคิดว่าคุณอาจพบว่าสิ่งนี้น่าสนใจ: github.com/REASY/akka-http-vs-akka-grpcในกรณีของเขา (และฉันคิดว่าสิ่งนี้ครอบคลุมถึงของคุณ) คอขวดอาจเกิดจากการใช้หน่วยความจำสูงใน protobuf ( ) การทำให้เป็นอันดับซึ่งจะทำให้มีการเรียกไปยังตัวเก็บรวบรวมขยะมากขึ้น
Batato

ขอบคุณที่น่าสนใจแม้ฉันจะแก้ไขปัญหาของฉันแล้ว
simpadjo

0

ฉันค่อนข้างประทับใจกับประสิทธิภาพของ Akka TCP ที่นี่: D

ประสบการณ์ของเราแตกต่างกันเล็กน้อย เรากำลังทำงานกับอินสแตนซ์ที่เล็กกว่ามากโดยใช้ Akka Cluster สำหรับการถอดความ Akka เราเปลี่ยนจาก Akka TCP เป็น UDP โดยใช้ Artery และได้รับอัตราที่สูงขึ้นมาก + ลดเวลาตอบสนองที่เสถียรลง แม้จะมีการกำหนดค่าใน Artery ช่วยปรับสมดุลระหว่างปริมาณการใช้ CPU และเวลาตอบสนองตั้งแต่เริ่มเย็น

ข้อเสนอแนะของฉันคือการใช้เฟรมเวิร์กที่ใช้ UDP ซึ่งดูแลความน่าเชื่อถือในการส่งสำหรับคุณ (เช่น Artery UDP) และเพียงแค่เรียงลำดับโดยใช้ Protobuf แทนที่จะใช้ gRPC แบบเต็มเนื้อ ช่องสัญญาณ HTTP / 2 นั้นไม่ได้มีวัตถุประสงค์เพื่อตอบสนองต่อปริมาณงานที่น้อยมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.