บางครั้งมากขึ้นก็น้อยลง เมื่อลดโหลดส่งผลให้เวลาแฝงเพิ่มขึ้น

เช่นเดียวกับใน โพสต์มากที่สุดมีปัญหากับบริการแบบกระจาย เรียกบริการนี้ว่า Alvin ครั้งนี้ฉันไม่ได้ค้นพบปัญหาด้วยตัวเอง เจ้าหน้าที่จากฝั่งไคลเอ็นต์บอกฉัน

วันหนึ่งฉันตื่นขึ้นมาด้วยอีเมลไม่พอใจเนื่องจากความล่าช้าอันยาวนานกับ Alvin ซึ่งเราวางแผนจะเปิดตัวในอนาคตอันใกล้นี้ โดยเฉพาะอย่างยิ่ง ลูกค้าประสบกับเวลาแฝงที่ 99 ในภูมิภาค 50 มิลลิวินาที ซึ่งสูงกว่างบประมาณเวลาแฝงของเราอย่างมาก สิ่งนี้น่าประหลาดใจเมื่อฉันทดสอบบริการอย่างกว้างขวาง โดยเฉพาะอย่างยิ่งเรื่องความล่าช้าซึ่งเป็นเรื่องร้องเรียนทั่วไป

ก่อนที่ฉันจะนำ Alvin เข้าสู่การทดสอบ ฉันได้ทำการทดสอบจำนวนมากโดยใช้การสืบค้น 40 ครั้งต่อวินาที (QPS) ซึ่งทั้งหมดแสดงเวลาแฝงที่น้อยกว่า 10ms ฉันพร้อมที่จะประกาศว่าฉันไม่เห็นด้วยกับผลลัพธ์ของพวกเขา แต่เมื่อดูจดหมายอีกครั้ง ฉันสังเกตเห็นสิ่งใหม่: ฉันไม่ได้ทดสอบเงื่อนไขที่พวกเขากล่าวถึงอย่างแน่นอน QPS ของพวกเขาต่ำกว่าของฉันมาก ฉันทดสอบที่ 40 QPS แต่อยู่ที่ 1 เท่านั้น ฉันทำการทดลองอีกครั้ง คราวนี้ใช้ QPS ที่ต่ำกว่าเพียงเพื่อเอาใจพวกเขา

เนื่องจากฉันกำลังเขียนบล็อกเกี่ยวกับเรื่องนี้ คุณคงทราบแล้วว่าตัวเลขของพวกเขาถูกต้อง ฉันทดสอบไคลเอนต์เสมือนของฉันซ้ำแล้วซ้ำอีก โดยผลลัพธ์เดียวกัน: คำขอจำนวนน้อยไม่เพียงเพิ่มเวลาแฝงเท่านั้น แต่ยังเพิ่มจำนวนคำขอด้วยเวลาแฝงมากกว่า 10 ms กล่าวอีกนัยหนึ่ง หากที่ 40 QPS ประมาณ 50 คำขอต่อวินาทีเกิน 50 ms ดังนั้นที่ 1 QPS ก็จะมี 100 คำขอที่สูงกว่า 50 ms ต่อวินาที พาราด็อกซ์!

บางครั้งมากขึ้นก็น้อยลง เมื่อลดโหลดส่งผลให้เวลาแฝงเพิ่มขึ้น

การจำกัดการค้นหาให้แคบลง

เมื่อต้องเผชิญกับปัญหาความล่าช้าในระบบแบบกระจายที่มีส่วนประกอบจำนวนมาก ขั้นตอนแรกคือการสร้างรายชื่อผู้ต้องสงสัยโดยย่อ มาเจาะลึกลงไปในสถาปัตยกรรมของ Alvin กันหน่อย:

บางครั้งมากขึ้นก็น้อยลง เมื่อลดโหลดส่งผลให้เวลาแฝงเพิ่มขึ้น

จุดเริ่มต้นที่ดีคือรายการการเปลี่ยนแปลง I/O ที่เสร็จสมบูรณ์ (การโทรผ่านเครือข่าย/การค้นหาดิสก์ ฯลฯ) ลองหาดูว่าความล่าช้าอยู่ที่ไหน นอกจาก I/O ที่ชัดเจนกับไคลเอนต์แล้ว Alvin ยังก้าวไปอีกขั้น: เขาเข้าถึงที่เก็บข้อมูล อย่างไรก็ตาม พื้นที่จัดเก็บข้อมูลนี้ทำงานในคลัสเตอร์เดียวกันกับ Alvin ดังนั้นเวลาแฝงจึงควรน้อยกว่ากับไคลเอ็นต์ ดังนั้นรายชื่อผู้ต้องสงสัย:

  1. การโทรผ่านเครือข่ายจากไคลเอนต์ถึงอัลวิน
  2. การโทรผ่านเครือข่ายจากอัลวินไปยังที่เก็บข้อมูล
  3. ค้นหาบนดิสก์ในที่เก็บข้อมูล
  4. การโทรผ่านเครือข่ายจากคลังข้อมูลถึงอัลวิน
  5. การโทรผ่านเครือข่ายจาก Alvin ไปยังไคลเอนต์

ลองขีดฆ่าบางจุดกัน

การจัดเก็บข้อมูลไม่เกี่ยวอะไรกับมัน

สิ่งแรกที่ฉันทำคือแปลง Alvin เป็นเซิร์ฟเวอร์ ping-ping ที่ไม่ประมวลผลคำขอ เมื่อได้รับการร้องขอ มันจะส่งคืนการตอบกลับที่ว่างเปล่า หากเวลาแฝงลดลง แสดงว่าข้อผิดพลาดในการใช้งาน Alvin หรือคลังข้อมูลก็ไม่เคยเกิดขึ้นมาก่อน ในการทดลองแรกเราจะได้กราฟดังนี้:

บางครั้งมากขึ้นก็น้อยลง เมื่อลดโหลดส่งผลให้เวลาแฝงเพิ่มขึ้น

อย่างที่คุณเห็น ไม่มีการปรับปรุงเมื่อใช้เซิร์ฟเวอร์ ping-ping ซึ่งหมายความว่าคลังข้อมูลจะไม่เพิ่มเวลาแฝง และรายชื่อผู้ต้องสงสัยก็ลดลงครึ่งหนึ่ง:

  1. การโทรผ่านเครือข่ายจากไคลเอนต์ถึงอัลวิน
  2. การโทรผ่านเครือข่ายจาก Alvin ไปยังไคลเอนต์

ยอดเยี่ยม! รายการกำลังหดตัวอย่างรวดเร็ว ฉันคิดว่าฉันเกือบจะเข้าใจเหตุผลแล้ว

ก.ร.ป

ตอนนี้เป็นเวลาที่จะแนะนำคุณให้รู้จักกับผู้เล่นใหม่: ก.ร.ป. นี่คือไลบรารีโอเพ่นซอร์สจาก Google สำหรับการสื่อสารระหว่างดำเนินการ RPC. แม้ว่า gRPC ได้รับการปรับปรุงมาอย่างดีและใช้กันอย่างแพร่หลาย นี่เป็นครั้งแรกที่ฉันใช้มันกับระบบขนาดนี้ และฉันคาดว่าการใช้งานของฉันจะไม่ค่อยดีนัก - พูดให้น้อยที่สุด

ความพร้อมใช้งาน gRPC ในสแต็กทำให้เกิดคำถามใหม่: อาจเป็นการใช้งานของฉันหรือตัวฉันเอง gRPC ทำให้เกิดปัญหาความล่าช้า? เพิ่มผู้ต้องสงสัยรายใหม่เข้ารายการ:

  1. ลูกค้าโทรเข้าห้องสมุด gRPC
  2. ห้องสมุด gRPC ทำการโทรผ่านเครือข่ายไปยังไลบรารีบนไคลเอนต์ gRPC บนเซิร์ฟเวอร์
  3. ห้องสมุด gRPC ติดต่ออัลวิน (ไม่มีการดำเนินการในกรณีเซิร์ฟเวอร์ปิงปอง)

เพื่อให้คุณเห็นภาพว่าโค้ดมีลักษณะอย่างไร การใช้งานไคลเอนต์/Alvin ของฉันไม่ได้แตกต่างจากไคลเอนต์-เซิร์ฟเวอร์มากนัก ตัวอย่างอะซิงก์.

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

การทำโปรไฟล์จะแก้ไขทุกสิ่ง

หลังจากขีดฆ่าที่เก็บข้อมูลแล้ว ฉันคิดว่าเกือบจะเสร็จแล้ว: “ตอนนี้มันง่ายมาก! ลองใช้โปรไฟล์และดูว่าความล่าช้าเกิดขึ้นที่ใด” ฉัน แฟนตัวยงของการจัดทำโปรไฟล์ที่มีความแม่นยำเพราะ CPU นั้นเร็วมากและส่วนใหญ่มักจะไม่ใช่ปัญหาคอขวด ความล่าช้าส่วนใหญ่เกิดขึ้นเมื่อโปรเซสเซอร์ต้องหยุดการประมวลผลเพื่อทำอย่างอื่น การทำโปรไฟล์ CPU ที่แม่นยำไม่เพียงแต่: บันทึกทุกอย่างได้อย่างแม่นยำ สวิตช์บริบท และทำให้ชัดเจนว่าความล่าช้าเกิดขึ้นที่ใด

ฉันใช้สี่โปรไฟล์: ด้วย QPS สูง (เวลาแฝงต่ำ) และเซิร์ฟเวอร์ปิงปองที่มี QPS ต่ำ (เวลาแฝงสูง) ทั้งบนฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์ และเผื่อไว้ ฉันก็เอาตัวอย่างโปรไฟล์โปรเซสเซอร์ไปด้วย เมื่อเปรียบเทียบโปรไฟล์ ฉันมักจะมองหา Call Stack ที่ผิดปกติ ตัวอย่างเช่น ในด้านที่ไม่ดีซึ่งมีเวลาแฝงสูง มีการสลับบริบทอีกมากมาย (10 ครั้งขึ้นไป) แต่ในกรณีของฉัน จำนวนสวิตช์บริบทเกือบจะเท่ากัน ด้วยความสยองขวัญของฉัน ไม่มีอะไรสำคัญที่นั่น

การดีบักเพิ่มเติม

ฉันหมดหวัง ฉันไม่รู้ว่าใช้เครื่องมืออื่นใดได้บ้าง และแผนถัดไปของฉันคือทำการทดลองซ้ำโดยใช้รูปแบบต่างๆ กัน แทนที่จะวินิจฉัยปัญหาอย่างชัดเจน

เกิดอะไรขึ้นถ้า

ตั้งแต่แรกเริ่ม ฉันกังวลเกี่ยวกับเวลาแฝง 50ms ที่เฉพาะเจาะจง นี่เป็นช่วงเวลาที่ยิ่งใหญ่มาก ฉันตัดสินใจว่าจะตัดโค้ดออกเป็นชิ้น ๆ จนกว่าฉันจะทราบได้อย่างชัดเจนว่าส่วนใดที่ทำให้เกิดข้อผิดพลาดนี้ จากนั้นก็มีการทดลองที่ได้ผล

ตามปกติเมื่อมองย้อนกลับไปทุกอย่างชัดเจน ฉันวางไคลเอนต์ไว้ในเครื่องเดียวกับ Alvin - และส่งคำขอไปที่ localhost. และความล่าช้าที่เพิ่มขึ้นก็หมดไป!

บางครั้งมากขึ้นก็น้อยลง เมื่อลดโหลดส่งผลให้เวลาแฝงเพิ่มขึ้น

มีบางอย่างผิดปกติกับเครือข่าย

การเรียนรู้ทักษะวิศวกรเครือข่าย

ฉันต้องยอมรับ: ความรู้ของฉันเกี่ยวกับเทคโนโลยีเครือข่ายแย่มาก โดยเฉพาะอย่างยิ่งเมื่อพิจารณาถึงความจริงที่ว่าฉันทำงานกับพวกเขาทุกวัน แต่เครือข่ายคือผู้ต้องสงสัยรายใหญ่ และฉันต้องเรียนรู้วิธีแก้ไขจุดบกพร่อง

โชคดีที่อินเทอร์เน็ตรักผู้ที่ต้องการเรียนรู้ การรวมกันของ ping และ Tracert ดูเหมือนจะเป็นการเริ่มต้นที่ดีพอที่จะแก้ไขปัญหาการขนส่งเครือข่าย

ประการแรกฉันเปิดตัว ป.ปิ๊ง ไปยังพอร์ต TCP ของ Alvin ฉันใช้การตั้งค่าเริ่มต้น - ไม่มีอะไรพิเศษ จากการปิงมากกว่าหนึ่งพันครั้ง ไม่มีครั้งใดที่เกิน 10 มิลลิวินาที ยกเว้นอันแรกสำหรับการอุ่นเครื่อง สิ่งนี้ตรงกันข้ามกับการเพิ่มขึ้นของเวลาแฝงที่สังเกตได้ 50 มิลลิวินาทีที่เปอร์เซ็นไทล์ที่ 99 ในกรณีนี้ ทุกๆ คำขอ 100 รายการ เราควรเห็นคำขอประมาณหนึ่งคำขอโดยมีเวลาแฝง 50 มิลลิวินาที

จากนั้นฉันก็พยายาม tracert: อาจมีปัญหาที่โหนดใดโหนดหนึ่งตามเส้นทางระหว่างอัลวินและไคลเอนต์ แต่ผู้ตามก็กลับมามือเปล่าเช่นกัน

ดังนั้นจึงไม่ใช่โค้ดของฉัน การใช้ gRPC หรือเครือข่ายที่ทำให้เกิดความล่าช้า ฉันเริ่มกังวลว่าฉันจะไม่มีวันเข้าใจเรื่องนี้

ตอนนี้เราอยู่ใน OS อะไร

gRPC ใช้กันอย่างแพร่หลายบน Linux แต่แปลกใหม่บน Windows ฉันตัดสินใจลองการทดลองซึ่งได้ผล: ฉันสร้างเครื่องเสมือน Linux คอมไพล์ Alvin สำหรับ Linux และปรับใช้

บางครั้งมากขึ้นก็น้อยลง เมื่อลดโหลดส่งผลให้เวลาแฝงเพิ่มขึ้น

และนี่คือสิ่งที่เกิดขึ้น: เซิร์ฟเวอร์ปิงปอง Linux ไม่มีความล่าช้าเหมือนกับโฮสต์ Windows ที่คล้ายกัน แม้ว่าแหล่งข้อมูลจะไม่แตกต่างกันก็ตาม ปรากฎว่าปัญหาอยู่ในการใช้งาน gRPC สำหรับ Windows

อัลกอริทึมของ Nagle

ตลอดเวลานี้ ฉันคิดว่าฉันพลาดธงไป gRPC. ตอนนี้ฉันเข้าใจแล้วว่ามันคืออะไรจริงๆ gRPC ธง Windows หายไป ฉันพบไลบรารี RPC ภายในที่ฉันมั่นใจว่าจะทำงานได้ดีกับการตั้งค่าสถานะทั้งหมด Winsock. จากนั้นฉันก็เพิ่มแฟล็กเหล่านี้ทั้งหมดลงใน gRPC และปรับใช้ Alvin บน Windows ในเซิร์ฟเวอร์ปิงปอง Windows ที่ได้รับแพตช์!

บางครั้งมากขึ้นก็น้อยลง เมื่อลดโหลดส่งผลให้เวลาแฝงเพิ่มขึ้น

เกือบจะ เสร็จสิ้น: ฉันเริ่มลบแฟล็กที่เพิ่มออกทีละรายการจนกว่าการถดถอยจะกลับมาเพื่อที่ฉันจะได้ระบุสาเหตุได้ มันน่าอับอาย TCP_NODELAY, สวิตช์อัลกอริทึมของ Nagle

อัลกอริทึมของ Nagle พยายามลดจำนวนแพ็กเก็ตที่ส่งผ่านเครือข่ายโดยชะลอการส่งข้อความจนกว่าขนาดแพ็กเก็ตจะเกินจำนวนไบต์ที่กำหนด แม้ว่าสิ่งนี้อาจดีสำหรับผู้ใช้ทั่วไป แต่ก็สร้างความเสียหายให้กับเซิร์ฟเวอร์แบบเรียลไทม์ได้ เนื่องจากระบบปฏิบัติการจะทำให้ข้อความบางข้อความล่าช้า ทำให้เกิดความล่าช้าใน QPS ที่ต่ำ ยู gRPC การตั้งค่าสถานะนี้ถูกตั้งค่าในการใช้งาน Linux สำหรับซ็อกเก็ต TCP แต่ไม่ใช่ใน Windows ฉันนี่แหละ แก้ไขแล้ว.

ข้อสรุป

เวลาแฝงที่สูงขึ้นที่ QPS ต่ำนั้นเกิดจากการเพิ่มประสิทธิภาพระบบปฏิบัติการ เมื่อมองย้อนกลับไป การทำโปรไฟล์ตรวจไม่พบเวลาแฝงเนื่องจากทำในโหมดเคอร์เนลแทนที่จะเป็นในโหมด โหมดผู้ใช้. ฉันไม่รู้ว่าอัลกอริทึมของ Nagle สามารถสังเกตได้จากการจับ ETW หรือไม่ แต่มันจะน่าสนใจ

สำหรับการทดลอง localhost นั้น อาจไม่ได้สัมผัสกับรหัสเครือข่ายจริง และอัลกอริทึมของ Nagle ไม่ทำงาน ดังนั้นปัญหาเวลาในการตอบสนองจึงหมดไปเมื่อไคลเอนต์เข้าถึง Alvin ผ่านทาง localhost

ครั้งต่อไปที่คุณเห็นเวลาแฝงเพิ่มขึ้นเมื่อจำนวนคำขอต่อวินาทีลดลง อัลกอริธึมของ Nagle ควรอยู่ในรายชื่อผู้ต้องสงสัยของคุณ!

ที่มา: will.com

เพิ่มความคิดเห็น