บทความ

ลองนึกภาพตามนะ ศุกร์เย็นแล้ว พาร์เซอร์หรือระบบจัดการบัญชีของคุณเพิ่งปล่อยลงโปรดักชั่น ไม่กี่นาทีแรกทุกอย่างลื่นไหล แต่แล้วความประหลาดก็เริ่มเกิดขึ้น: คำขอบางส่วนล้มเหลว, แคปชาโผล่มาที่นี่ที่นั่น, บัญชีบางบัญชีอยู่ดีๆ ก็ขอให้ยืนยันตัวตน, และล็อกกลายเป็นเรื่องเละเทะเต็มไปด้วย error การเชื่อมต่อ ใช่ไหม? เก้าในสิบครั้ง ต้นตอของปัญหาไม่ได้อยู่ที่เว็บไซต์เป้าหมายหรือที่โลจิกธุรกิจของคุณ แต่อยู่ที่วิธีที่แอปพลิเคชันของคุณจัดการกับที่อยู่ IP ของมัน

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

บทความนี้คือคู่มือฉบับสมบูรณ์สำหรับการออกแบบ พูลพร็อกซีภายในแอปพลิเคชัน เราจะเจาะลึกถึงวิธีเลือกที่อยู่ให้เหมาะกับงานแต่ละประเภท, วิธีตรวจสอบสุขภาพของที่อยู่, วิธีนำ IP ที่มีปัญหาออกมากักกันแล้วนำกลับมาใช้ใหม่, และวิธีรักษา sticky-เซสชัน ไว้กับบัญชีเดียว เป็นหัวข้อเชิงวิศวกรรม แต่เราจะพูดถึงมันด้วยภาษาที่เข้าใจง่าย พร้อมโค้ด, เช็กลิสต์ และการวิเคราะห์ข้อผิดพลาดจริง

มากำหนดขอบเขตกันก่อน เราจะไม่พูดถึง time-out หรือนโยบายการลองซ้ำสำหรับคำขอเดียว เพราะนั่นเป็นหัวข้อใหญ่ที่แยกต่างหาก เราจะไม่พูดถึงวิธีการเปลี่ยนที่อยู่บนโมเด็มหรืออุปกรณ์จริงๆ ด้วย เพราะนั่นเป็นเรื่องของโครงสร้างพื้นฐาน ไม่ใช่ของแอปพลิเคชัน โฟกัสของเราคือโลจิกของพูลในโค้ดของคุณ

ทำไมลิสต์ที่อยู่ในไฟล์ข้อความถึงพังเมื่อเจอโหลดจริง

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

ปัญหาแรก: ไม่มีการจดจำสถานะของที่อยู่

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

ปัญหาที่สอง: ไม่มีการผูกงานกับที่อยู่

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

ปัญหาที่สาม: Race condition เมื่อมีหลาย worker

ทันทีที่คุณรันหลายกระบวนการหรือหลายเธรดพร้อมกัน อาร์เรย์ธรรมดาในหน่วยความจำจะกลายเป็นแหล่งของ race condition Worker สองตัวอ่านอินเด็กซ์เดียวกัน, ทั้งคู่เลือกที่อยู่เดียวกัน, ทั้งคู่โหลดมันหนักเป็นสองเท่า ไม่มีการประสานงานเลย ตัวนับถ้ามีก็จะหายไประหว่างกระบวนการ

ปัญหาที่สี่: ไม่สามารถสังเกตการณ์ได้

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

ข้อสรุปง่ายๆ ลิสต์ที่อยู่คือข้อมูล แต่การจัดการที่อยู่ภายใต้โหลดคือระบบ ความแตกต่างระหว่างสองสิ่งนี้คือหัวข้อของบทความเรา ต่อจากนี้เราจะสร้างระบบนี้ทีละส่วน

พื้นฐาน: พูล, การเช่าที่อยู่สำหรับงาน, และอะไรที่เรียกว่าเซสชัน

เริ่มจากรากฐานก่อน ถ้าคุณเป็นวิศวกรมากประสบการณ์ก็แนะนำให้อ่านเหมือนกัน เพราะนี่คือการตกลงคำศัพท์ที่เราจะใช้ไปจนจบบทความ

พูลที่อยู่คืออะไร

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

การเช่าที่อยู่สำหรับงาน

การเช่า คือการผูกที่อยู่กับงานเฉพาะชั่วคราว พูลจะจ่ายที่อยู่, ทำเครื่องหมายว่าถูกครอบครองหรือบันทึกว่ามีโหลดใหม่เพิ่ม, และเมื่องานเสร็จก็คืนที่อยู่ นี่คือเหตุผลที่อินเทอร์เฟซของพูลมีสองเมธอดที่จะเป็นม้าศึกของเรา: acquire - รับที่อยู่, และ release - คืนที่อยู่พร้อมรายงานผล

การรายงานเมื่อคืนเป็นรายละเอียดสำคัญ เมื่องานคืนที่อยู่ มันจะบอกพูลว่าทุกอย่างจบลงอย่างไร: สำเร็จ, error การเชื่อมต่อ, แคปชา, หรือถูกบล็อก ข้อมูลนี้ป้อนเข้าสู่โลจิกสุขภาพและการกักกันทั้งหมดที่ตามมา

Sticky-ผูก vs. การสุ่มเลือก

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

Sticky-ผูก คือการที่คีย์หนึ่งๆ เช่น ID บัญชี จะได้รับที่อยู่เดิมเสมอ สิ่งนี้สำคัญมากเมื่อความต่อเนื่องของตัวตนในช่วงเวลามีความสำคัญ การทำงานกับบัญชีเป็นตัวอย่างทั่วไป บัญชีควรปรากฏต่อแพลตฟอร์มเป็นผู้ใช้ที่มั่นคงคนเดียว ไม่ใช่เป็นฝูงของเอนทิตีที่กระโดดไปมาในเครือข่าย

อะไรคือเซสชันในระดับงานธุรกิจ

คำว่า เซสชัน ใช้ในหลายความหมาย ในระดับ HTTP ก็อย่างนึง ในระดับ TCP ก็อีกอย่างนึง แต่สิ่งที่เราสนใจคือเซสชันทางธุรกิจ มันคือลำดับการกระทำที่เชื่อมโยงกันทางตรรกะ ซึ่งจากมุมมองของแพลตฟอร์ม ควรมาจากผู้ใช้คนเดียวกันและที่อยู่เดียวกัน

ตัวอย่างเซสชันทางธุรกิจ:

  • เข้าสู่ระบบบัญชี, ทำชุดการกระทำภายใน, ออกจากระบบ - ทั้งหมดจากที่อยู่เดียว
  • สถานการณ์หลายขั้นตอน: เปิดหน้ารายละเอียดสินค้า, เพิ่มลงตะกร้า, ชำระเงิน - ทุกขั้นตอนเชื่อมโยงกัน
  • การทำงานภายใต้โปรไฟล์เดียวตลอดวันทำงาน การเปลี่ยนที่อยู่จะดูน่าสงสัยเหมือนย้ายบ้านกระทันหัน

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

เจาะลึก: วงจรชีวิตของที่อยู่ในพูล

ก่อนที่จะแยกวิเคราะห์กลยุทธ์ต่างๆ ควรเห็นภาพรวมก่อน ที่อยู่ทุกแห่งในพูลที่ออกแบบมาอย่างดีจะมีวงจรชีวิต ซึ่งก็คือชุดของสถานะที่มันเปลี่ยนผ่าน

สถานะของที่อยู่

  • Healthy (แข็งแรง) - ที่อยู่พร้อมให้บริการ, เมตริกปกติ
  • Degraded (เสื่อมสภาพ) - ที่อยู่ยังให้บริการอยู่ แต่มีน้ำหนักลดลง เพราะตัวบ่งชี้แย่ลง
  • Quarantined (ถูกกักกัน) - ที่อยู่ถูกนำออกจากการให้บริการชั่วคราว, กำลังรอเวลาพัก
  • Probing (กำลังตรวจสอบ) - ที่อยู่กำลังผ่านการตรวจสอบแบบแอคทีฟก่อนกลับเข้าประจำการ
  • Dead (ตาย) - ที่อยู่ถูก判定ว่าใช้ไม่ได้เป็นเวลานานหรือถาวร

การเปลี่ยนผ่านระหว่างสถานะเหล่านี้คือระบบที่เรากำลังสร้างนั่นเอง ที่อยู่ที่แข็งแรงเมื่อสะสมข้อผิดพลาดก็จะเสื่อมสภาพ ที่อยู่ที่เสื่อมสภาพเมื่อถึงเกณฑ์ก็จะถูกกักกัน จากกักกันหลังจากพักแล้วก็จะไปตรวจสอบ ผ่านการตรวจสอบก็กลับมาแข็งแรง ไม่ผ่านก็กลับไปกักกันโดยเพิ่มเวลาพัก ล้มเหลวติดต่อกันหลายครั้งก็จะถูก判定ว่าตาย

ทำไมต้องมีเลเยอร์ของนามธรรม

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

แบบจำลองทางความคิดของโหลด

ควรจำไว้ในใจสามแกนที่ที่อยู่ดำเนินไป:

  • ความสด - นานแค่ไหนแล้วที่เราตรวจสอบสุขภาพของมันครั้งล่าสุด
  • โหลด - มีงานกี่งานที่กำลังทำงานอยู่บนนั้น
  • ชื่อเสียง - ประวัติสะสมของความสำเร็จและความล้มเหลว

กลยุทธ์การเลือกใดๆ โดยพื้นฐานแล้วเป็นการรวมสามแกนนี้เป็นการประเมินเดียว ต่อไปเราจะวิเคราะห์กลยุทธ์เฉพาะ

กลยุทธ์การเลือกที่อยู่: จาก round-robin ถึง hash ตามคีย์

หัวใจของพูลคืออัลกอริทึมที่ตัดสินใจว่าจะจ่ายที่อยู่ใดในการเรียก acquire แต่ละครั้ง ไม่มีคำตอบที่ถูกต้องเพียงข้อเดียว การเลือกกลยุทธ์ขึ้นอยู่กับงาน มาวิเคราะห์สี่วิธีพื้นฐานและอธิบายว่าเมื่อไหร่ควรใช้

Round-robin: วนไปเรื่อยๆ

Round-robin คืออัลกอริทึมที่ยุติธรรมที่สุด ที่อยู่ถูกจัดเรียงเป็นวงกลม และตัวชี้จะเลื่อนไปทีละอันในการเรียกแต่ละครั้ง วนครบรอบก็เริ่มใหม่ ข้อดีคือกระจายโหลดอย่างสม่ำเสมอเมื่อที่อยู่เหมือนกัน ข้อเสียคือมันตาบอดต่อความแตกต่างระหว่างที่อยู่ ที่อยู่เร็วและช้าจะได้รับส่วนแบ่งทราฟฟิกเท่ากัน

เมื่อไหร่ควรใช้: พูลที่เหมือนกัน, งานที่ไม่มีเซสชัน, เมื่อที่อยู่ทั้งหมดมีคุณภาพและกำลังใกล้เคียงกัน

การเลือกแบบถ่วงน้ำหนัก

การเลือกแบบถ่วงน้ำหนัก คือการพัฒนา round-robin โดยกำหนดน้ำหนักให้แต่ละที่อยู่ ที่อยู่ที่มีน้ำหนักมากจะได้รับทราฟฟิกมากกว่า น้ำหนักสามารถกำหนดแบบสแตติกตามแบนด์วิดท์ที่ทราบ หรือแบบไดนามิกโดยคำนวณจากเมตริกสด เช่น อัตราความสำเร็จและความหน่วง

น้ำหนักแบบไดนามิกเป็นเครื่องมือที่ทรงพลัง ที่อยู่เริ่มคืนแคปชาบ่อยขึ้น? ลดน้ำหนักลง มันจะได้รับทราฟฟิกน้อยลงแต่ไม่หลุดออกไปทั้งหมด ตัวชี้วัดฟื้นตัว? น้ำหนักก็เพิ่มกลับมา นี่คือการควบคุมตนเองแบบนุ่มนวล ซึ่งอ่อนโยนกว่าการนำออกไปกักกันอย่างกระทันหัน

สูตรง่ายๆ สำหรับน้ำหนัก: น้ำหนัก = อัตราความสำเร็จ / (ความหน่วงที่ทำให้เป็นมาตรฐาน) ยิ่งอัตราความสำเร็จสูงและความหน่วงต่ำ น้ำหนักยิ่งมาก คำนวณใหม่บนหน้าต่างเลื่อน เช่น จากห้าสิบคำขอล่าสุด

Least-connections: โหลดน้อยที่สุด

Least-connections จ่ายที่อยู่ที่มีงานกำลังทำงานน้อยที่สุดในขณะนั้น วิธีนี้ทำงานได้ยอดเยี่ยมเมื่องานมีความยาวแตกต่างกันมาก Round-robin ในสถานการณ์เช่นนี้อาจถ่วงที่อยู่หนึ่งด้วยงานยาวๆ ในขณะที่อีกที่อยู่หนึ่งว่างเปล่า Least-connections จะปรับสมดุลโหลดจริงเอง ไม่ใช่แค่จำนวนคำขอที่แจกไป

ในการใช้งานต้องมีตัวนับจำนวนการเช่าที่กำลังทำงานอยู่บนแต่ละที่อยู่ มันเพิ่มขึ้นเมื่อ acquire และลดลงเมื่อ release เลือกที่อยู่ที่มีตัวนับต่ำที่สุด โปรดทราบ: ตัวนับนี้เป็นสถานะที่ใช้ร่วมกัน และเมื่อมีหลาย worker มันต้องอยู่ในที่เก็บส่วนกลาง เราจะกลับมาที่เรื่องนี้ในส่วนของสถานะ

Hash ตามคีย์: หนึ่งบัญชี – หนึ่งที่อยู่เสมอ

และนี่คือประเด็นหลักสำหรับการทำงานกับบัญชี Hash ตามคีย์ คือกลยุทธ์ที่เลือกที่อยู่ไม่ใช่แบบสุ่ม แต่เป็นแบบ deterministic ตามคีย์ของงาน เอา ID บัญชี, คำนวณ hash, หารเอาเศษด้วยจำนวนที่อยู่ – ก็จะได้อินเด็กซ์ บัญชีเดิมจะให้อินเด็กซ์เดิมเสมอ ซึ่งหมายถึงที่อยู่เดิม

ทำไมมันไม่ใช่แค่สะดวก แต่สำคัญโดยพื้นฐาน? นี่คือข้อมูลเชิงลึกสำคัญของทั้งบทความ

ทำไม deterministic hash ถึงสำคัญกว่าการสุ่มสำหรับการป้องกันการโกง

ระบบป้องกันการโกงของแพลตฟอร์มจะสร้างโปรไฟล์พฤติกรรมของผู้ใช้ หนึ่งในสัญญาณความน่าเชื่อถือที่แข็งแกร่งที่สุดคือความเสถียรของสภาพแวดล้อมเครือข่าย คนจริงๆ มักจะออกอินเทอร์เน็ตจากที่อยู่ชุดเดิมโดยประมาณ ผู้ให้บริการของเขาเปลี่ยนไม่บ่อย ที่ตั้งทางภูมิศาสตร์คงที่

ทีนี้ ลองนึกภาพว่าบัญชีหนึ่งในทุกการกระทำส่องที่อยู่ใหม่จากซับเน็ตอื่น สำหรับระบบมันดูเหมือนพฤติกรรมที่เป็นไปไม่ได้: คนเราไม่สามารถอยู่ในสิบสถานที่ภายในหนึ่งนาทีได้ การสุ่มเลือกที่อยู่รับประกันว่าจะสร้างธงแดงแบบนี้

Deterministic hash แก้ปัญหาที่ต้นตอ บัญชีถูกผูกกับที่อยู่ทางคณิตศาสตร์ โดยไม่ต้องเก็บสถานะ แม้แอปพลิเคชันรีสตาร์ทและสูญเสียความจำทั้งหมด บัญชีเดิมก็จะคำนวณที่อยู่เดิมได้อีกครั้ง นี่คือการผูกที่ซ่อมแซมตัวเองได้ สวยงามจริงไหม?

ข้อควรระวัง: การเปลี่ยนแปลงขนาดพูล

hash แบบ naive โดยการหารเอาเศษมีจุดอ่อนร้ายแรง ถ้าจำนวนที่อยู่เปลี่ยนไป – เพิ่มหนึ่งตัวหรือเอาไปกักกัน – เศษจากการหารจะเปลี่ยนไปเกือบทุกคีย์ บัญชีเกือบทั้งหมดจะย้ายไปยังที่อยู่ใหม่อย่างกระทันหัน นั่นคือหายนะที่เราพยายามหลีกเลี่ยง

ทางแก้คือ consistent hashing เป็นเทคนิคที่การเพิ่มหรือลบที่อยู่หนึ่งตัวจะ reassign แค่คีย์ส่วนน้อย ไม่ใช่ทั้งหมด ที่อยู่และคีย์ถูกวางบนวงแหวนสมมุติ คีย์เดินไปตามวงแหวนจนเจอที่อยู่ใกล้ที่สุด ลบที่อยู่ออก – เฉพาะคีย์ของมันที่ต้องย้าย ส่วนที่เหลืออยู่ที่เดิม Consistent hashing คือรากฐานที่ถูกต้องสำหรับ sticky-ผูกในพูลที่มีที่อยู่เข้า-ออกตลอดเวลา

การรวมกลยุทธ์

ในแอปพลิเคชันจริง กลยุทธ์มักถูกผสมผสาน สถานการณ์ทั่วไปขั้นสูง: สำหรับงานที่มีคีย์บัญชี ใช้ consistent hash และภายในกลุ่มที่อยู่ที่รับผิดชอบการสำรอง ใช้ least-connections สำหรับการเก็บข้อมูลแบบไม่มีเซสชัน ใช้ weighted round-robin พูลสามารถมีหลายกลยุทธ์และเลือกตามประเภทของงาน

เช็กลิสต์การเลือกกลยุทธ์

  • มีแนวคิดของเซสชันหรือการผูกกับบัญชี? ใช้ hash ตามคีย์ โดยเฉพาะ consistent hash
  • งานอิสระและเหมือนกัน? Round-robin
  • ที่อยู่มีคุณภาพต่างกัน? การเลือกแบบถ่วงน้ำหนักด้วยน้ำหนักไดนามิก
  • งานมีความยาวแตกต่างกันมาก? Least-connections
  • โหลดผสม? รวมกลยุทธ์โดยแบ่งพูลเป็นกลุ่มย่อย

Health-check: การตรวจสอบสุขภาพแบบพาสซีฟและแอคทีฟ

พูลจะดีเท่าที่มันรู้สถานะของที่อยู่ของมัน ดังนั้นจึงต้องมีกลไกตรวจสอบสุขภาพ health-check มีสองวิธีที่เสริมกัน: พาสซีฟและแอคทีฟ จำเป็นต้องมีทั้งสอง

Health-check แบบพาสซีฟจาก error ของทราฟฟิก

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

สัญญาณที่บ่งบอกถึงการเสื่อมสภาพในการตรวจสอบแบบพาสซีฟ:

  • Error การเชื่อมต่อ: ที่อยู่ไม่ตอบสนอง, หลุดการเชื่อมต่อ
  • คำตอบที่บ่งบอกถึงการถูกบล็อกในระดับเครือข่าย
  • แคปชาพุ่งสูง – เป็นสัญญาณทางอ้อมแต่สำคัญของชื่อเสียงที่ตกต่ำของที่อยู่
  • ความหน่วงเพิ่มขึ้นอย่างรวดเร็วเมื่อเทียบกับค่าปกติทางประวัติศาสตร์ของที่อยู่นั้น

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

Health-check แบบแอคทีฟผ่านการ probe

การตรวจสอบแบบแอคทีฟ คือการ probe แยกต่างหากที่พูลทำเอง โดยไม่ขึ้นกับทราฟฟิกการทำงาน โดยปกติเป็นการเรียกเบาๆ ไปยังทรัพยากรควบคุมที่รู้จักซึ่งตอบสนองสม่ำเสมอ และช่วยให้判断ว่าที่อยู่ทำงานได้หรือไม่

Active probe แก้สิ่งที่ passive ทำไม่ได้: ตรวจสอบที่อยู่ที่ว่างงานและที่อยู่ในกักกันก่อนนำกลับมา เป็น active probe นั่นเองที่เป็นด่านที่ตัดสินว่าจะปล่อยที่อยู่ออกจากกักกันกลับมาให้บริการหรือไม่

อะไรที่ถือว่าล้มเหลวในการตรวจสอบ

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

  • Error เดียว – ไม่ใช่ความล้มเหลว นั่นคือ noise เครือข่ายไม่น่าเชื่อถือโดยธรรมชาติ
  • ความล้มเหลวคือสัญญาณสะสม เช่น error สามครั้งจากห้าคำขอล่าสุด หรืออัตราความสำเร็จในหน้าต่างลดลงต่ำกว่าเจ็ดสิบเปอร์เซ็นต์
  • ควรแยกสัดส่วนแคปชาต่างหาก เกณฑ์ที่นี่ต่ำกว่า เพราะแคปชาเป็นสัญญาณเกี่ยวกับชื่อเสียงของที่อยู่โดยเฉพาะ ไม่ใช่ความผิดพลาดสุ่ม

ช่วงเวลาที่ควรใช้

ช่วงเวลาของ active probe เป็นเรื่องสมดุลระหว่างความสดของข้อมูลกับโหลดเพิ่มเติม แนวทางทั่วไป:

  • ที่อยู่ที่แข็งแรงและอยู่ภายใต้โหลด ไม่ต้อง probe แบบ active เลย เพราะ passive traffic บอกอยู่แล้ว
  • ที่อยู่ที่แข็งแรงแต่ว่างงาน – probe ทุกสามสิบถึงหกสิบวินาที เพื่อให้มันพร้อมใช้งาน
  • ที่อยู่ในกักกัน – probe ตามตารางเวลาพักซึ่งจะกล่าวถึงด้านล่าง
  • อย่า probe ที่อยู่ทั้งหมดพร้อมกัน กระจาย probe ตามเวลา เพิ่มการเลื่อนแบบสุ่ม เพื่อไม่ให้สร้างการพุ่งขึ้นพร้อมกัน

Flapping และวิธีระงับด้วย hysteresis

ทีนี้มาถึงปรากฏการณ์ที่ถูกประเมินต่ำที่สุดอย่างหนึ่ง Flapping คือเมื่อที่อยู่สลับไปมาระหว่างสถานะแข็งแรงและป่วยอย่างรวดเร็ว Probe ผ่าน – กลับเข้าประจำการ ทันใดนั้น error – เอาออก วินาทีถัดมา probe ผ่านอีก – กลับเข้าอีก เป็นวงจรนี้เรื่อยไป สร้างความเหนื่อยหน่ายให้ระบบ, ทำให้เมตริกกระตุก, และไม่ให้ที่อยู่ได้ทำงานหรือพักอย่างเหมาะสม

การรักษาคือ hysteresis ศัพท์จากอิเล็กทรอนิกส์ หมายถึงเกณฑ์ที่แตกต่างกันสำหรับการเข้าและออกจากสถานะ แนวคิดง่ายๆ: ต้องใช้สัญญาณน้อยกว่าในการ判定ว่าที่อยู่ป่วย มากกว่าในการ判定ว่ากลับมาแข็งแรงอีกครั้ง เช่น error สามครั้งติดต่อกันนำไปกักกัน แต่การจะกลับมา ต้องผ่าน probe สำเร็จห้าครั้งติดกัน ความไม่สมมาตรของเกณฑ์สร้าง zone แห่งความเสถียร ซึ่งการแกว่งเล็กน้อยไม่เปลี่ยนสถานะ

เครื่องมือที่สองต้าน flapping คือ เวลาพักในสถานะ ที่อยู่ที่ถูกกักกันต้องอยู่ที่นั่นอย่างน้อยตามเวลาที่กำหนด แม้ว่า probe จะผ่านก่อนหน้านี้ สิ่งนี้ระงับการแกว่งเร็ว การรวม hysteresis และเวลาพักขั้นต่ำทำให้ระบบประสาทกลายเป็นระบบที่สงบและคาดเดาได้

เช็กลิสต์ health-check

  • มีการตรวจสอบทั้งสองแบบ: passive ผ่านทราฟฟิก และ active ผ่าน probe
  • ความล้มเหลวนิยามเป็นสัญญาณสะสม ไม่ใช่ error เดียว
  • สำหรับสัดส่วนแคปชา ตั้งเกณฑ์แยกต่างหากที่ไวขึ้น
  • Active probe กระจายตามเวลาด้วยการเลื่อนแบบสุ่ม
  • ตั้งค่า hysteresis: เกณฑ์เข้าเกณฑ์ปัญหาต่ำกว่าเกณฑ์ออก
  • ตั้งเวลาพักขั้นต่ำในสถานะเพื่อต้าน flapping

การกักกันและการกลับมาประจำการ: การพัก, ขีดจำกัด, และการป้องกันพูล

เมื่อที่อยู่ถูก判定ว่ามีปัญหา มันไม่สามารถแค่โยนทิ้งไปได้ ปัญหามักเป็นชั่วคราว หน้าที่ของการกักกันคือให้ที่อยู่ได้พักและจากนั้นตรวจสอบว่ามันฟื้นตัวหรือไม่ ที่นี่มีวิศวกรรมละเอียดอ่อนมากมาย

การพักแบบ exponential

การกักกันแบบ naive กักที่อยู่ไว้ตามเวลาคงที่ เช่น หนึ่งนาที แล้วส่งกลับ แต่ถ้าที่อยู่มีปัญหาอย่างต่อเนื่อง คุณจะส่งมันกลับซ้ำแล้วซ้ำเล่า ทุกครั้งก็จะเจอความล้มเหลวอีก ทางแก้คือ การพักแบบ exponential

หลักการ: ทุกครั้งที่ถูกกักกันซ้ำ เวลาพักจะเพิ่มขึ้น ครั้งแรก – หนึ่งนาที ครั้งที่สองติดต่อกัน – สองนาที จากนั้นสี่, แปด, สิบหก จนถึงเพดานที่เหมาะสม เช่น หนึ่งชั่วโมง เมื่อที่อยู่ทำงานสำเร็จอย่างต่อเนื่องนานพอ ตัวนับการพักก็จะรีเซ็ตเป็นค่าเริ่มต้น

นี่แก้ปัญหาสองอย่างพร้อมกันอย่างสวยงาม ที่อยู่ที่สะดุดชั่วคราวจะกลับมาเร็ว ส่วนที่อยู่ที่ป่วยเรื้อรังจะรบกวนระบบน้อยลงเรื่อยๆ จนกลายเป็น dead โดยพฤตินัย โดยไม่ทำให้พูลรกด้วย probe ที่ไร้ประโยชน์บ่อยครั้ง

เพิ่มการกระจายแบบสุ่มหรือ jitter เข้าไปในการพัก หากไม่มี ที่อยู่ทั้งหมดที่ถูกกักกันในเวลาเดียวกันจะกลับมาเป็นระลอกเดียว Jitter จะกระจายการกลับมาในเวลา

ขีดจำกัดจำนวนที่อยู่ที่ถูกนำออกพร้อมกัน

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

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

การป้องกันสถานการณ์ที่พูลทั้งหมดถูกกักกัน

นี่เป็นต่อเนื่องจากแนวคิดก่อนหน้า จนถึงกรณีสุดขั้ว ลองนึกภาพ: probe ไปยังทรัพยากรควบคุมเสียเอง ทรัพยากรล่มหรือเปลี่ยนคำตอบ พูล判定ว่าที่อยู่ทั้งหมดตาย และนำพูลทั้งหมดเข้ากักกัน แอปพลิเคชันหยุดทำงานสนิท ทั้งที่จริงๆ แล้วที่อยู่ปกติ

กลไกป้องกัน:

  • รับประกันขั้นต่ำในประจำการ ให้มีที่อยู่อย่างน้อยหนึ่งถึงสองแห่งพร้อมใช้งานเสมอ แม้ว่าจะผ่านการตรวจสอบอย่างเป็นทางการไม่สำเร็จก็ตาม ปล่อยให้มันทำงานภายใต้ข้อกังขา ดีกว่าระบบหยุดทำงาน
  • การวิเคราะห์สหสัมพันธ์ ถ้าความล้มเหลวเกิดขึ้นพร้อมกันทุกที่อยู่ – มันน่าสงสัย เป็นไปได้ว่าปัจจัยร่วมเสีย: probe, เครือข่ายฝั่งคุณ, ทรัพยากรเป้าหมาย พูลควรสามารถรับรู้ความล้มเหลวหมู่และไม่ตื่นตระหนก ไม่นำที่อยู่ทั้งหมดออก
  • แยกทรัพยากรควบคุม อย่าผูก active probe ไว้กับจุดควบคุมเดียว ถ้ามันกลายเป็นจุดเสียจุดเดียว การแจ้งเตือนเท็จจะล่มพูลทั้งหมด

การกลับมาประจำการผ่านสถานะกึ่งเปิด

การกลับจากการกักกันไม่ใช่ทันที แนวปฏิบัติที่ดีคือ สถานะกึ่งเปิด ซึ่งเป็นแนวคิดจากแพทเทิร์น circuit breaker ที่อยู่ที่ออกจากกักกันไม่ได้รับทราฟฟิกเต็มที่ในทันที ตอนแรกให้ส่วนแบ่งเล็ก ๆ เป็นสายธารทดสอบ ถ้าคำขอทดสอบสำเร็จ ก็ค่อย ๆ เพิ่มส่วนแบ่งจนกระทั่งที่อยู่ได้รับน้ำหนักเต็มที่ ถ้าการทดสอบล้มเหลว ก็กลับไปกักกันด้วยเวลาพักที่เพิ่มขึ้น

การ ramp-up แบบนุ่มนวลนี้ป้องกันการยิงทราฟฟิกจำนวนมากไปยังที่อยู่ที่ยังไม่ฟื้นตัวเต็มที่

เช็กลิสต์การกักกัน

  • เวลาพักเพิ่มขึ้นแบบ exponential เมื่อถูกกักกันซ้ำ
  • เพิ่ม jitter ในการพักเพื่อป้องกันการกลับมาพร้อมกัน
  • ตั้งขีดจำกัดสัดส่วนที่อยู่ที่ถูกกักกันพร้อมกัน
  • รับประกันที่อยู่ขั้นต่ำในประจำการภายใต้ทุกสภาวะ
  • มีการรับรู้ความล้มเหลวหมู่เป็นสัญญาณของปัญหาทั่วไป
  • การกลับมาผ่านสถานะกึ่งเปิดด้วยการเพิ่มทราฟฟิกแบบค่อยเป็นค่อยไป

การจัดเก็บสถานะ: ความจำของกระบวนการ vs. ที่เก็บส่วนกลาง

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

ความจำของกระบวนการ: ง่าย แต่อยู่คนเดียว

ถ้าแอปพลิเคชันทำงานในกระบวนการเดียว สถานะพูลที่ง่ายที่สุดคือเก็บในหน่วยความจำ โครงสร้างข้อมูลทั่วไป: dictionary ของที่อยู่, ตัวนับ และตัวจับเวลา เร็ว, ไม่มี dependency, ไม่มีความหน่วงของเครือข่าย

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

### ที่เก็บส่วนกลาง: Redis และ Memcached

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

Redis เป็นที่นิยมมากกว่า Memcached ในกรณีส่วนใหญ่ เพราะมี operation อะตอมมิก, โครงสร้างข้อมูลอย่าง sorted set และ hash, ตัวนับแบบ increment อะตอมมิก, และความสามารถในการรันสคริปต์เล็ก ๆ แบบอะตอมมิก ทั้งหมดนี้มีประโยชน์

Race condition เมื่อมีหลาย worker

ที่เก็บส่วนกลางแก้ปัญหาการมองเห็น แต่สร้างปัญหาใหม่ – race condition สถานการณ์คลาสสิกของ least-connections: worker สองตัวอ่านตัวนับพร้อมกัน, ทั้งคู่เห็นว่าที่อยู่ X มีโหลดน้อยที่สุด, ทั้งคู่เลือกมัน, ทั้งคู่เพิ่มตัวนับ ผลลัพธ์คือที่อยู่ได้รับโหลดสองเท่า ทั้งที่อัลกอริทึมควรหลีกเลี่ยงสิ่งนี้

วิธีแก้:

  • Operation อะตอมมิก การ increment ตัวนับใน Redis เป็นอะตอมมิกโดยธรรมชาติ ใช้สิ่งนี้แทนการอ่าน, เพิ่ม, แล้วเขียนแยกกัน
  • สคริปต์ โลจิกการเลือกที่ซับซ้อนที่ต้องอ่านหลายค่าและตัดสินใจ ควรทำเป็นสคริปต์อะตอมมิกเดียวบนที่เก็บ เพื่อไม่ให้ใครมาแทรกระหว่างอ่านกับเขียน
  • ล็อคแบบกระจาย สำหรับ critical section สามารถใช้ล็อคสั้น ๆ ได้ แต่ระวัง – ล็อคกระทบประสิทธิภาพและอาจเป็นแหล่งปัญหาด้วยตัวเอง Operation อะตอมมิกเกือบจะดีกว่าเสมอ

TTL ของเรคคอร์ด

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

ที่ไหนควรใช้ TTL:

  • Sticky-ผูกคีย์กับที่อยู่ จำเซสชันธุรกิจได้ไหม? ความยาวของมันคือ TTL ของเรคคอร์ดการผูก ตั้ง TTL สิบห้านาที – และหลังจากไม่มีการเคลื่อนไหวสิบห้านาที การผูกจะหายไปเอง บัญชีในการเรียกครั้งต่อไปจะได้รับการผูกใหม่ นี่คือวิธีที่สะอาดและสง่างามในการแสดงเวลาชีวิตของเซสชัน
  • ตัวนับการเช่าที่กำลังทำงาน ถ้า worker ล้มและไม่เรียก release ตัวนับอาจค้างสูงตลอดไป TTL หรือการตรวจสอบเป็นระยะช่วยป้องกัน มักจะตั้งค่าเช่าเป็นเรคคอร์ดที่มี TTL ยาวกว่าระยะเวลาสูงสุดของงานเล็กน้อย เพื่อที่ worker ที่ค้างจะไม่ยึดที่อยู่ตลอดไป
  • สถานะกักกัน เวลาพักสามารถแสดงผ่าน TTL ได้โดยธรรมชาติ: เรคคอร์ดกักกันมีชีวิตอยู่ตราบเท่าที่เวลาพัก และเมื่อหมดก็หายไป เปิดทางให้กลับมา

แนวทางแบบไฮบริด

ในทางปฏิบัติ มักใช้แบบไฮบริด ข้อมูลร้อนที่อ่านบ่อยจะถูกแคชในหน่วยความจำของ worker ในระยะสั้น ในขณะที่ source of truth ยังคงเป็นที่เก็บส่วนกลาง ซึ่งลดจำนวนการเรียกไปยัง Redis แต่ต้องระวังเรื่องการสูญเสีย synchronization ข้อประนีประนอมที่ดีคือ local cache ที่มี TTL สั้นมาก เช่น หนึ่งถึงสองวินาที สำหรับเมตริกที่ไม่ต้องการความแม่นยำทันที

การสังเกตการณ์: เมตริกต่อที่อยู่ และวิธีแยก IP แย่ออกจากเว็บไซต์แย่

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

เมตริกต่อที่อยู่

ชุดขั้นต่ำที่ควรติดตามสำหรับแต่ละที่อยู่บนหน้าต่างเลื่อน:

  • อัตราความสำเร็จ สัดส่วนของความสำเร็จต่อจำนวนงานทั้งหมด ตัวชี้วัดสุขภาพหลักแบบรวม
  • ความหน่วง ไม่ใช่แค่ค่าเฉลี่ย แต่ควรติดตามเปอร์เซ็นไทล์ด้วย มัธยฐานและเช่น 95th เปอร์เซ็นไทล์บอกเรื่อง tail ได้มากกว่าค่าเฉลี่ยที่ถูกบิดเบือนโดยค่าผิดปกติได้ง่าย
  • สัดส่วนแคปชา เมตริกที่แยกต่างหากและมีความหมายมาก การเพิ่มขึ้นของสัดส่วนแคปชาบนที่อยู่คือสัญญาณเริ่มต้นของชื่อเสียงที่ตกต่ำ มักมาก่อนที่ error โดยตรงจะเพิ่มขึ้น
  • จำนวนการเช่าที่กำลังทำงาน โหลดปัจจุบัน จำเป็นสำหรับ least-connections และเพื่อเข้าใจการกระจาย
  • ประวัติการกักกัน กี่ครั้งและนานเท่าใดที่ที่อยู่ถูกกักกัน ผู้กระทำผิดเรื้อรังเห็นได้ทันที

เมตริกของพูลโดยรวม

  • สัดส่วนที่อยู่ในแต่ละสถานะ: แข็งแรง, เสื่อมสภาพ, ถูกกักกัน, ตาย
  • ปริมาณงานรวมและอัตราความสำเร็จรวม
  • ความถี่ของการถูกกักกันตามเวลา – การพุ่งขึ้นบ่งบอกถึงเหตุการณ์
  • ระดับความเต็มของขีดจำกัดการกักกัน – การเข้าใกล้เป็นสัญญาณเตือน

วิธีแยก IP แย่ออกจากเว็บไซต์แย่

นี่คือคำถามที่แยกระบบที่เติบโตเต็มที่ออกจากระบบ naive Error เริ่มเท – ใครผิด? ที่อยู่ที่มีปัญหาหรือแพลตฟอร์มเป้าหมายที่ชั่วคราวไม่พร้อมสำหรับทุกคน? ถ้าสับสน คุณจะเริ่มกักที่อยู่ที่แข็งแรงเพราะความผิดของเซิร์ฟเวอร์อื่น

วิธีการแยก diagnosis – การวิเคราะห์สหสัมพันธ์:

  • ปัญหาที่ที่อยู่เดียว ถ้า error และแคปชากระจุกตัวอยู่ที่หนึ่งหรือไม่กี่ที่อยู่ ในขณะที่ที่เหลือทำงานปกติ – ที่อยู่ผิด นำมันเข้ากักกัน
  • ปัญหาทุกที่อยู่พร้อมกันต่อแพลตฟอร์มเดียว ถ้าอัตราความสำเร็จลดลงทันทีทุกที่อยู่ต่อแพลตฟอร์มเป้าหมายเฉพาะ แต่ต่อแพลตฟอร์มอื่นยังดี – ไม่ใช่พูลผิด แต่เป็นแพลตฟอร์มนั้น การนำที่อยู่ออกกักกันไร้ประโยชน์และเป็นอันตราย
  • ปัญหาทุกที่อยู่ต่อทุกแพลตฟอร์ม เป็นไปได้ว่าปัญหาอยู่ฝั่งคุณ: เครือข่าย, โครงสร้างพื้นฐาน, ระบบ probe เอง ก็ไม่ควรโทษที่อยู่

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

ล็อกและ trace

นอกจากเมตริกรวม การล็อกการตัดสินใจของพูลก็มีประโยชน์: ทำไมที่อยู่ถูกเลือก, ทำไมที่อยู่นั้นถูกส่งไปกักกัน เมื่อมีอะไรผิดพลาด ร่องรอยการตัดสินใจเหล่านี้จะเป็นห่วงชูชีพของคุณ อย่าล็อกทุกรายละเอียดของทุกคำขอ – คุณจะจมน้ำ ให้ล็อกการเปลี่ยนสถานะและการตัดสินใจที่ผิดปกติ

ปฏิบัติ: โครงกระดูกพูลบน Python และ Node.js

จากทฤษฎีสู่โค้ด มาวิเคราะห์ส่วนสำคัญของการ implement บนสองแพลตฟอร์มยอดนิยม นี่คือโครงกระดูก, โครงร่าง, ที่คุณจะนำไปต่อเติมเฉพาะของโปรเจกต์ ตัดรายละเอียดออกเพื่อความชัดเจนของแนวคิดหลัก: อินเทอร์เฟซ acquire และ release, การเลือกที่อยู่, และการบันทึกผล

อินเทอร์เฟซ: acquire และ release

ตกลงสัญญากันก่อน เมธอด acquire รับคีย์ที่ไม่บังคับ – เช่น ID บัญชีสำหรับ sticky-ผูก – และคืนที่อยู่ เมธอด release รับที่อยู่และผลลัพธ์ของงาน ผลลัพธ์อธิบายขั้นต่ำด้วยสองข้อเท็จจริง: สำเร็จหรือไม่ และพบสัญญาณแคปชาหรือการบล็อกหรือไม่

โครงกระดูกบน Python

พิจารณาพูลแบบง่ายบน Python เก็บโลจิกในหน่วยความจำเพื่อความชัดเจน แต่ระบุว่าที่ไหนควรเชื่อมต่อที่เก็บส่วนกลาง

องค์ประกอบสำคัญของโครงสร้างข้อมูล: dictionary ที่ key คือที่อยู่, value คือออบเจ็กต์สถานะที่มีฟิลด์ชื่อเสียง, จำนวนการเช่าที่กำลังทำงาน, timestamp การออกจากกักกัน และระยะเวลาพักปัจจุบัน แยกเก็บตาราง sticky-ผูก key-ที่อยู่

Pseudocode ของเมธอด acquire ในภาษาแบบ Python มีลักษณะดังนี้ ก่อนอื่นตรวจสอบว่ามี key หรือไม่ ถ้ามี key และมีการผูกที่ยังมีชีวิตและที่อยู่ที่ผูกแข็งแรง – คืนที่อยู่นั้น, ต่ออายุ TTL การผูก ถ้าไม่มีการผูก – เลือกที่อยู่ด้วย consistent hash จาก key ในกลุ่มที่แข็งแรง, บันทึกการผูกด้วย TTL, คืนที่อยู่ ถ้าไม่มี key เลย – ใช้กลยุทธ์สำหรับงานที่ไม่มีเซสชัน เช่น การเลือกแบบถ่วงน้ำหนักจากกลุ่มที่แข็งแรง ในทุกกรณี increment ตัวนับการเช่าที่กำลังทำงานของที่อยู่ที่เลือก

Pseudocode release: decrement ตัวนับการเช่าที่กำลังทำงาน อัปเดตหน้าต่างเลื่อนชื่อเสียงตามผล – เพิ่มความสำเร็จหรือความล้มเหลว, แยกบันทึกสัญญาณแคปชา ถ้าสัญญาณสะสมข้ามเกณฑ์ความล้มเหลวโดยพิจารณา hysteresis – ย้ายที่อยู่เข้ากักกัน: บันทึกสถานะ, คำนวณเวลาพัก = พื้นฐานคูณสองยกกำลังจำนวนครั้งที่ถูกกักกันซ้ำ, จำกัดด้วยเพดาน, เพิ่ม jitter, ตั้ง TTL หรือ timestamp การกลับมา ถ้าผลดีและที่อยู่เสถียรมานาน – รีเซ็ตตัวนับการกักกันซ้ำ

ลูปพื้นหลังแยกต่างหาก ทุกช่วงเวลา วนผ่านที่อยู่ในกักกันที่หมดเวลาพักแล้ว, และเริ่ม active probe ถ้าผ่านตาม hysteresis ครบจำนวนครั้งที่ต้องการ – ย้ายที่อยู่ไปสถานะกึ่งเปิดด้วยน้ำหนักน้อย, จากนั้นค่อยๆ เป็นแข็งแรง ถ้าไม่ผ่าน – ขยายเวลากักกันด้วยเวลาพักที่เพิ่มขึ้น

รายละเอียดปฏิบัติสำคัญในการ implement Python: ใช้ asynchrony ถ้ามีงานขนานกันมาก, ห่อหุ้มการเข้าถึงโครงสร้างที่ใช้ร่วมกันด้วย synchronization ที่เหมาะสม, และเมื่อย้ายไปหลายกระบวนการ ให้แทนที่ dictionary ภายในด้วยการเรียกไปยัง Redis ผ่านคำสั่งอะตอมมิกและสคริปต์ ตัวนับการเช่าที่กำลังทำงานเหมาะกับการ increment แบบอะตอมมิก, การผูก key-ที่อยู่เหมาะกับเรคคอร์ดที่มี TTL, ชุดที่อยู่ตามสถานะสะดวกที่จะเก็บใน sorted set ที่น้ำหนักของที่อยู่คือค่าประเมิน

โครงกระดูกบน Node.js

ใน Node.js โมเดล asynchronous เป็นธรรมชาติ และพูลมักถูก implement เป็นคลาสที่มีเมธอด async acquire และ release ที่คืน promise แนวคิดเหมือนกัน แต่เปลี่ยนสำนวน

โครงสร้างสถานะ – object-dictionary ของที่อยู่, ที่ value มีชื่อเสียงเป็น ring buffer ของผลล่าสุด, ตัวนับการเช่าที่กำลังทำงาน, และฟิลด์กักกัน สำหรับหลาย worker ซึ่งใน Node มักเป็น cluster ของกระบวนการ สถานะก็ต้องย้ายไป Redis เช่นกัน เพราะแต่ละกระบวนการถูกแยกจากกัน

เมธอด acquire ใน Node: ตรวจสอบ sticky-ผูกผ่านที่เก็บแบบ asynchronous, ถ้ามีการผูกที่มีชีวิตและแข็งแรง – คืนและต่ออายุ TTL มิฉะนั้นเลือกที่อยู่ – สำหรับ key ใช้ consistent hash, สำหรับงานไม่มีเซสชันใช้แบบถ่วงน้ำหนัก เพิ่มตัวนับการเช่าแบบอะตอมมิก คืนที่อยู่

เมธอด release ใน Node: ลดตัวนับการเช่าแบบอะตอมมิก เขียนผลลงในหน้าต่างชื่อเสียง ตรวจสอบเกณฑ์ด้วย hysteresis และหากล้มเหลว ตั้งค่ากักกันด้วยเวลาพักแบบ exponential และ jitter, ปฏิบัติตามขีดจำกัดจำนวนที่อยู่ที่ถูกกักกันทั้งหมด – ถ้าถึงขีดจำกัด แทนที่จะกักกันให้ลดน้ำหนัก

การตรวจสอบพื้นหลังใน Node สะดวกด้วย periodic timer ที่เลือกที่อยู่ที่หมดเวลาพักและเริ่ม active probe, กระจายตามเวลา Probe ที่สำเร็จจะค่อยๆ คืนที่อยู่ โดยเพิ่มน้ำหนักทีละขั้น

คำแนะนำทั่วไปสำหรับทั้งสองแพลตฟอร์ม: อย่าพยายามทำให้สมบูรณ์แบบในครั้งเดียว เริ่มต้นด้วย round-rolin บวก passive health-check บวกกักกันอย่างง่ายในหน่วยความจำ ทำให้แน่ใจว่าอินเทอร์เฟซ acquire และ release สะดวกสำหรับโลจิกธุรกิจของคุณ จากนั้นค่อยเพิ่ม sticky ผ่าน hash, active probe, ที่เก็บส่วนกลาง, ขีดจำกัด และการสังเกตการณ์ ให้แต่ละเลเยอร์ได้พิสูจน์ตัวเองภายใต้โหลดจริง

มินิเช็กลิสต์การ implement

  • อินเทอร์เฟซลดเหลือ acquire พร้อม key ไม่บังคับ และ release พร้อมผลลัพธ์
  • ความซับซ้อนของสถานะทั้งหมดซ่อนอยู่ในพูล โค้ดธุรกิจไม่รู้เรื่อง
  • Sticky implement ผ่าน deterministic hash โดยเฉพาะ consistent hash
  • ตัวนับและการผูกเป็นอะตอมมิกเมื่อมีหลาย worker
  • มีลูปพื้นหลังสำหรับ active probe ของที่อยู่ในกักกันและที่ว่างงาน
  • เมตริกถูกเขียนทุกครั้งที่ release

ข้อผิดพลาดทั่วไป: สิ่งที่ไม่ควรทำ

ทฤษฎีเข้าใจแล้ว โครงกระดูกสร้างเสร็จ ทีนี้มาดูคราดที่คนเหยียบซ้ำแล้วซ้ำเล่า ความรู้ข้อผิดพลาดเหล่านี้จะประหยัดเวลาดีบักเป็นสัปดาห์

พูลรวมสำหรับหลายแพลตฟอร์ม

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

สิ่งที่ถูกต้องคือ เก็บชื่อเสียงในมุมมองของคู่ที่อยู่-แพลตฟอร์ม และแยกพูลหรือกลุ่มย่อยตามตรรกะสำหรับทิศทางต่างๆ จากนั้นการตัดสินใจกักกันจะถูกต้องและยุติธรรม

ไม่มีขีดจำกัดงานต่อที่อยู่

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

กำหนดเพดานจำนวนการเช่าพร้อมกันต่อที่อยู่ เมื่อถึงเพดาน – ที่อยู่จะถูกแยกออกจากผู้สมัครชั่วคราว ทราฟฟิกไปที่อื่น ข้อจำกัดง่ายๆ นี้ช่วยป้องกันปัญหามากมาย

การหมุนเวียนกลางเซสชัน

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

การป้องกัน: ตราบใดที่เซสชันยังดำเนินอยู่ การผูกคีย์กับที่อยู่ต้องไม่ถูกละเมิด แม้ที่อยู่จะเสื่อมสภาพเล็กน้อย ให้เปลี่ยนการผูกเฉพาะที่ขอบเขตของเซสชัน – เมื่อมันสิ้นสุดโดยธรรมชาติหรือ TTL หมด และถ้าที่อยู่ตายจริงๆ และไม่มีทางออก – ควรจบเซสชันอย่างถูกต้อง ดีกว่าย้ายมันไปที่อยู่อื่นกลางคัน

ข้อผิดพลาดทั่วไปอื่นๆ

  • Error เดียว = โทษประหาร นำที่อยู่ออกกักกันเพราะความผิดพลาดสุ่มครั้งเดียว เครือข่ายไม่น่าเชื่อถือ การพลาดครั้งเดียวเป็นเรื่องปกติ ใช้สัญญาณสะสมเท่านั้น
  • ไม่มี hysteresis ทำให้เกิด flapping และความกระตุกอย่างที่เราคุยกัน
  • เวลาพักคงที่แทน exponential ที่อยู่ที่ป่วยเรื้อรังจะกลับมาตลอดและทำลายสถิติ
  • ไม่มีขีดจำกัดการกักกัน เหตุการณ์สามารถกักพูลทั้งหมดและหยุดระบบ
  • Probe พร้อมกัน ทุกที่อยู่ถูก probe พร้อมกัน สร้างการพุ่งของโหลด
  • สถานะในหน่วยความจำเท่านั้นเมื่อมีหลาย worker แต่ละกระบวนการอยู่ในความเป็นจริงของตัวเอง ไม่มีการประสานงาน
  • การเช่าค้าง ไม่มีการเรียก release เนื่องจากข้อบกพร่อง ตัวนับค้างสูง ที่อยู่ถูกนับว่าถูกครอบครองตลอดไป รักษาด้วย TTL สำหรับการเช่า
  • เชื่อมั่นในทรัพยากรควบคุมเดียวแบบมืดบอด มันล่ม – และพูลทั้งหมดคิดว่าตัวเองตาย

เครื่องมือและทรัพยากรสำหรับการ implement

มารวบรวมคลังแสงปฏิบัติ สิ่งที่จะมีประโยชน์จริงๆ ในการสร้างพูล

ที่เก็บสถานะ

  • Redis ตัวเลือกหลักสำหรับสถานะส่วนกลาง ตัวนับอะตอมมิก, sorted set สำหรับน้ำหนักและสถานะ, hash สำหรับ metadata ของที่อยู่, TTL ในตัว, สคริปต์สำหรับโลจิกซับซ้อนแบบอะตอมมิก เกือบจะเหมาะกับงานของเรา
  • Memcached เรียบง่ายและเบากว่า เหมาะถ้าต้องการแค่แคชพื้นฐานพร้อมตัวนับ แต่เสียเปรียบ Redis ในแง่ความหลากหลายของโครงสร้างและความเป็นอะตอมมิกของ operation ซับซ้อน

ไลบรารีและแนวทางตามระบบนิเวศ

  • Python สแต็ก asynchronous สำหรับงานขนาน, Redis client ที่รองรับ asynchrony และสคริปต์, implementation consistent hash พร้อมใช้, และแพทเทิร์น circuit breaker สำหรับสถานะกึ่งเปิด
  • Node.js คลาส asynchronous ที่เป็นธรรมชาติ, Redis client เติบโตเต็มที่, โมเดล cluster ของกระบวนการ ซึ่งสถานะส่วนกลางจำเป็น, ไลบรารี consistent hash และ implementation circuit breaker

การสังเกตการณ์

  • ระบบเมตริก time-series สำหรับเก็บตัวบ่งชี้อัตราความสำเร็จ, ความหน่วง และแคปชาตามที่อยู่และคู่ที่อยู่-แพลตฟอร์ม
  • Dashboard การแสดงภาพการกระจายสถานะพูล, เมทริกซ์ที่อยู่-แพลตฟอร์ม, ความถี่การกักกัน Dashboard จะช่วยให้เห็นได้ในพริบตาว่า IP แย่หรือแพลตฟอร์มแย่
  • Alert แจ้งเตือนเมื่อเข้าใกล้ขีดจำกัดการกักกัน, เมื่อเกิดความล้มเหลวหมู่, เมื่ออัตราความสำเร็จรวมต่ำกว่าเกณฑ์

สิ่งที่จะสร้างเอง

ไลบรารีพูลสำเร็จรูปที่ครอบคลุมทุกความแตกต่างของคุณอาจไม่มี – เพราะโลจิกธุรกิจของเซสชันเฉพาะตัวมาก ดังนั้นแกนพูลมักจะเขียนเอง โดยอาศัยอิฐที่กล่าวมา: ที่เก็บ, hashing, เมตริก, circuit breaker ข่าวดีคือเมื่ออินเทอร์เฟซ acquire และ release ถูกออกแบบอย่างระมัดระวัง แกนนี้จะกะทัดรัดและ reuse ได้ระหว่างโปรเจกต์

กรณีศึกษาและผลลัพธ์การใช้งาน

มาวิเคราะห์สถานการณ์ทั่วไปที่แสดงให้เห็นว่าหลักการในบทความทำงานอย่างไรในทางปฏิบัติ ตัวเลขเป็นตัวอย่าง แต่สัดส่วนสะท้อนพลวัตทั่วไป

กรณีแรก: เก็บข้อมูลสาธารณะแบบไม่มีเซสชัน

งาน – เก็บข้อมูลสาธารณะจำนวนมากจากหน้าที่ไม่เกี่ยวข้องกัน เริ่มด้วยการสุ่มเลือกจากไฟล์แบบ naive อัตราความสำเร็จอยู่ที่ประมาณเจ็ดสิบเปอร์เซ็นต์ เพราะที่อยู่ที่ตายแล้วยังคงได้รับทราฟฟิกเท่ากับที่อยู่ที่มีชีวิต

นำพูลที่มีการเลือกแบบถ่วงน้ำหนักและ passive health-check เข้ามา น้ำหนักของที่อยู่คำนวณใหม่จากหน้าต่างเลื่อนของอัตราความสำเร็จ ที่อยู่ที่ตายแล้วเสียweight เร็วและแทบไม่ได้รับงานอีก เพิ่ม active probe สำหรับที่อยู่ที่ว่างงาน และการกักกันแบบ exponential

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

กรณีที่สอง: ทำงานกับบัญชีและ sticky-เซสชัน

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

เปลี่ยนเป็นการผูกแบบ deterministic ผ่าน consistent hash จาก ID บัญชี, จัดเก็บการผูกใน Redis ด้วย TTL เท่ากับระยะเวลาเซสชันธุรกิจ ตั้งกฎตายตัว: ไม่มีการหมุนเวียนกลางเซสชัน การผูกเปลี่ยนที่ขอบเขตเท่านั้น

ผลลัพธ์: สัดส่วนแคปชาลดลงอย่างเห็นได้ชัด จำนวนคำขอยืนยันตัวตนลดลง พฤติกรรมของบัญชีดูเสถียรและเป็นธรรมชาติสำหรับแพลตฟอร์ม ข้อมูลเชิงลึกของกรณีนี้ – สำหรับการป้องกันการโกง ความสามารถในการคาดเดาของสภาพแวดล้อมเครือข่ายมีค่ามากกว่าการหมุนเวียนที่ซับซ้อนใดๆ

กรณีที่สาม: เหตุการณ์และการป้องกันการกักกันถล่ม

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

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

ผลลัพธ์: แทนที่จะหยุดทำงานสนิท ระบบผ่านเหตุการณ์ไปได้ด้วยการเสื่อมสภาพ ไม่ใช่ล้มเหลว ข้อมูลเชิงลึก – ความทนทานวัดจากพฤติกรรมในวันที่แย่ที่สุด ไม่ใช่วันที่ดีที่สุด

กรณีที่สี่: การวินิจฉัยที่อยู่แย่ vs. แพลตฟอร์มแย่

ทีมใช้เวลาหลายสัปดาห์ในการนำที่อยู่ที่แข็งแรงออกกักกันเป็นระยะๆ โดยไม่เข้าใจสาเหตุ เมตริกถูกติดตามแค่ตามที่อยู่ ไม่ได้ตัดตามแพลตฟอร์ม เมื่อเพิ่มเมทริกซ์สองมิติที่อยู่-แพลตฟอร์ม ภาพก็ชัดเจนทันที: ปัญหาไม่ได้อยู่ที่ที่อยู่ แต่อยู่ที่แพลตฟอร์มจอมแปรปรวนหนึ่งที่ให้ error พุ่งสำหรับทุกที่อยู่พร้อมกัน

ตั้งโลจิกให้ความล้มเหลวที่สัมพันธ์กันตามคอลัมน์แพลตฟอร์มไม่ลงโทษที่อยู่ ผลลัพธ์ – จำนวนการกักกันเท็จลดลงเกือบเป็นศูนย์ และความจุที่มีประโยชน์ของพูลเพิ่มขึ้นโดยไม่ต้องเพิ่มที่อยู่ใหม่แม้แต่แห่งเดียว ข้อมูลเชิงลึก – การตัดเมตริกอย่างถูกต้องบางครั้งมีค่ามากกว่าอัลกอริทึมใดๆ

คำถามที่พบบ่อย

จำเป็นต้องมีพูลหรือถ้ามีที่อยู่แค่ไม่กี่แห่ง?

แม้จะมีที่อยู่ไม่กี่แห่ง พูลก็คุ้มค่าทันทีที่มีโหลดหรือแนวคิดของเซสชันเกิดขึ้น Passive health-check และการกักกันง่ายๆ จะป้องกันคุณจากการยิงไปยังที่อยู่ที่ตายแล้ว Sticky-ผูกจะรักษาความเสถียรของบัญชี Consistent hash เต็มรูปแบบและที่เก็บส่วนกลางบนที่อยู่สองสามแห่งอาจมากเกินไป แต่พูลพื้นฐานในหน่วยความจำพร้อมอินเทอร์เฟซ acquire และ release ควรมีไว้เสมอ

จะเลือกความยาวของ sticky-เซสชันอย่างไร?

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

จะเกิดอะไรขึ้นถ้าที่อยู่ที่ผูกไว้ตายกลางเซสชัน?

นี่เป็นกรณีที่เลวร้ายที่สุด กฎทั่วไป – อย่าหมุนเวียนกลางเซสชัน ถ้าที่อยู่เสื่อมสภาพแต่ยังมีชีวิตอยู่ ให้ใช้มันจนจบเซสชัน ถ้ามันตายสนิท ควรจบหรือหยุดเซสชันไว้ก่อน ดีกว่าย้ายไปที่อยู่อื่นและสร้างเอฟเฟกต์ teleportation ออกแบบโลจิกธุรกิจให้การจบเซสชันเจ็บปวดน้อยกว่าการย้าย

ควรทำ active probe บ่อยแค่ไหน?

อย่า probe ที่อยู่ที่แข็งแรงและมีโหลด – ทราฟฟิกจริงบอกผ่าน passive control อยู่แล้ว ที่อยู่ที่แข็งแรงแต่ว่างงาน – ตรวจสอบทุกหลายสิบวินาที เพื่อให้พร้อมใช้งาน ที่อยู่ในกักกัน – probe ตามตารางเวลาพักแบบ exponential ต้องกระจาย probe ตามเวลาด้วย jitter เสมอ เพื่อไม่ให้สร้างการพุ่งพร้อมกัน

Redis จำเป็นหรือใช้หน่วยความจำก็พอ?

ถ้าคุณมีกระบวนการเดียวอย่างเคร่งครัดและยอมสูญเสียสถานะเมื่อรีสตาร์ท – หน่วยความจำก็ใช้ได้ในช่วงเริ่มต้น ทันทีที่มี worker ตัวที่สอง ที่เก็บส่วนกลางกลายเป็นสิ่งจำเป็น ไม่งั้นแต่ละกระบวนการจะอยู่ในความเป็นจริงที่ต่างกันโดยไม่มีการประสานงาน Redis เป็นตัวเลือกที่สะดวกที่สุดเพราะ operation อะตอมมิก, โครงสร้างข้อมูล และ TTL เริ่มต้นด้วยหน่วยความจำได้ แต่ควรออกแบบ abstraction ของที่เก็บไว้ เพื่อให้การย้ายไป Redis ไม่ต้องเขียนโค้ดใหม่ครึ่งหนึ่ง

จะแยกปัญหาที่อยู่ออกจากปัญหาแพลตฟอร์มเป้าหมายได้อย่างไร?

ติดตามเมตริกในมุมมองคู่ที่อยู่-แพลตฟอร์ม ไม่ใช่แค่ตามที่อยู่ ถ้า error กระจุกตัวที่ที่อยู่เดียวในทุกแพลตฟอร์ม – ที่อยู่ผิด ถ้า error เกิดขึ้นทุกที่อยู่ต่อแพลตฟอร์มเดียว – แพลตฟอร์มผิด อย่าลงโทษที่อยู่ ถ้า error เกิดขึ้นทุกที่อยู่ต่อทุกแพลตฟอร์ม – หาปัญหาที่ฝั่งคุณ: เครือข่าย, โครงสร้างพื้นฐาน, ระบบ probe เอง การตัดสองมิตินี้เป็นวิธีที่น่าเชื่อถือที่สุดในการวินิจฉัยที่ถูกต้อง

จะทำอย่างไรถ้าที่อยู่เกือบทั้งพูลถูกกักกัน?

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

กลยุทธ์การเลือกเริ่มต้นควรเป็นอะไร?

ถ้าไม่มีเซสชันและที่อยู่เหมือนกัน – เริ่มด้วย round-robin ถ้าที่อยู่คุณภาพต่างกัน – การเลือกแบบถ่วงน้ำหนักด้วยน้ำหนักไดนามิกตามเมตริก ถ้างานมีความยาวต่างกันมาก – least-connections ถ้ามีการผูกกับบัญชีหรือเซสชัน – consistent hash ตามคีย์ และนี่ไม่ต้องถกเถียง เพราะเป็นตัวกำหนดความเสถียรในการทำงานกับบัญชี ในระบบที่ซับซ้อน ให้รวมกลยุทธ์ตามประเภทงาน

จำเป็นต้องแยกนับแคปชาในเมตริกหรือไม่?

ใช่ จำเป็น และมีเกณฑ์ที่ไวต่อการเปลี่ยนแปลงมากกว่า error ทั่วไป แคปชาเป็นสัญญาณเริ่มต้นและแม่นยำของชื่อเสียงที่ตกต่ำของที่อยู่ มักมาก่อนการปฏิเสธโดยตรง ถ้าคุณรวมแคปชากับ error ทั่วไป คุณจะสูญเสียตัวบ่งชี้ล่วงหน้านี้ เมตริกแยกสัดส่วนแคปชาต่อที่อยู่จะช่วยลดน้ำหนักของที่อยู่ที่มีปัญหาก่อนที่มันจะเริ่มล้มเหลวอย่างโจ่งแจ้ง

จะหลีกเลี่ยงการเช่าค้างเมื่อ worker ล้มได้อย่างไร?

อย่าพึ่งพาแค่การเรียก release อย่างชัดแจ้ง กำหนดให้การเช่าเป็นเรคคอร์ดที่มี TTL มากกว่าระยะเวลาสูงสุดที่อนุญาตของงานเล็กน้อย ถ้า worker ล้มและไม่คืนที่อยู่ เรคคอร์ดจะหมดอายุเอง และตัวนับโหลดจะกลับคืนมา เสริมด้วยการตรวจสอบตัวนับกับความเป็นจริงเป็นระยะ สิ่งนี้ป้องกันการรั่วไหลของความจุอย่างช้าๆ เนื่องจากการเช่าค้าง

บทสรุปและขั้นตอนถัดไป

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

พูลไม่ใช่ลิสต์ แต่เป็นออบเจ็กต์ที่มีพฤติกรรม ซ่อนอยู่หลังอินเทอร์เฟซง่ายๆ acquire และ release กลยุทธ์การเลือกที่อยู่ขึ้นอยู่กับงาน: round-robin สำหรับงานที่เหมือนกัน, การเลือกแบบถ่วงน้ำหนักสำหรับที่อยู่คุณภาพต่างกัน, least-connections สำหรับงานความยาวต่างกัน, และ – สำคัญยิ่งสำหรับการทำงานกับบัญชี – deterministic โดยเฉพาะ consistent hash ตามคีย์ ความสามารถในการคาดเดาของสภาพแวดล้อมเครือข่าย ไม่ใช่ความสุ่มที่ซับซ้อน คือสิ่งที่สร้างความไว้วางใจจากระบบป้องกันการโกง

สุขภาพของที่อยู่ยืนบนสองขา: passive control ผ่านทราฟฟิกจริง และ active probe ความล้มเหลวนิยามด้วยสัญญาณสะสม ไม่ใช่ error เดียว และ flapping ถูกระงับด้วย hysteresis และเวลาพักขั้นต่ำ การกักกันสร้างบนเวลาพักแบบ exponential ด้วย jitter, จำกัดด้วยขีดจำกัดสัดส่วนที่อยู่ที่ถูกกักกัน, และป้องกันจากหายนะที่พูลทั้งหมดเสี่ยงถูกกักกัน สถานะเมื่อมีหลาย worker อยู่ในที่เก็บส่วนกลางด้วย operation อะตอมมิกและ TTL และการสังเกตการณ์ในมุมมองคู่ที่อยู่-แพลตฟอร์มช่วยแยกที่อยู่แย่ออกจากแพลตฟอร์มแย่ – ทักษะที่ประหยัดเวลาเป็นสัปดาห์

จะทำอะไรต่อ? ผมขอเสนอแผนการ implement ที่เป็นรูปธรรมเป็นขั้นตอน

  1. ห่อหุ้มการทำงานกับที่อยู่ปัจจุบันในอินเทอร์เฟซ acquire และ release โดยยังไม่เปลี่ยนอะไรภายใน นี่จะเตรียมพื้น
  2. เพิ่ม passive health-check และกักกันง่ายๆ ในหน่วยความจำ แค่นี้คุณก็จะรู้สึกถึงความเสถียรที่เพิ่มขึ้น
  3. นำกลยุทธ์การเลือกที่ต้องการเข้ามา ถ้าทำงานกับบัญชี ให้วาง sticky ผ่าน hash ตามคีย์ตั้งแต่แรก
  4. เชื่อมต่อ active probe และเวลาพักแบบ exponential พร้อมขีดจำกัดป้องกัน
  5. ย้ายสถานะไปยังที่เก็บส่วนกลางทันทีที่มี worker ตัวที่สอง วางอะตอมมิกและ TTL
  6. ตั้งค่าเมตริกและ dashboard โดยเฉพาะอย่างยิ่งการตัดที่อยู่-แพลตฟอร์ม
  7. ปรับแต่ง hysteresis และเกณฑ์ตามข้อมูลจริงของคุณ – ไม่มีตัวเลขสากล มีแต่การสังเกตของคุณเท่านั้น

อย่าพยายามสร้างทุกอย่างพร้อมกัน ให้แต่ละเลเยอร์ได้พิสูจน์ตัวเองภายใต้โหลด, เก็บเมตริก, สรุปผล, และก้าวต่อไป พูลพร็อกซีที่เติบโตเต็มที่ไม่ใช่การสร้างครั้งเดียว แต่เป็นระบบที่มีชีวิตที่คุณปรับแต่งไปพร้อมกับการเติบโตของงาน แต่แม้แต่ก้าวแรกจากคำแนะนำนี้ก็จะเปลี่ยนลิสต์ที่อยู่ที่เปราะบางให้เป็นรากฐานที่มั่นคงของแอปพลิเคชันของคุณ และนั่น, คุณคงเห็นด้วย, คุ้มค่ากับความพยายาม