พูลพร็อกซีในแอป: เลือก IP, ตรวจสุขภาพ, กักกัน และ sticky-เซสชัน
บทความ
- ทำไมลิสต์ที่อยู่ในไฟล์ข้อความถึงพังเมื่อเจอโหลดจริง
- พื้นฐาน: พูล, การเช่าที่อยู่สำหรับงาน, และอะไรที่เรียกว่าเซสชัน
- เจาะลึก: วงจรชีวิตของที่อยู่ในพูล
- กลยุทธ์การเลือกที่อยู่: จาก round-robin ถึง hash ตามคีย์
- Health-check: การตรวจสอบสุขภาพแบบพาสซีฟและแอคทีฟ
- การกักกันและการกลับมาประจำการ: การพัก, ขีดจำกัด, และการป้องกันพูล
- การจัดเก็บสถานะ: ความจำของกระบวนการ vs. ที่เก็บส่วนกลาง
- การสังเกตการณ์: เมตริกต่อที่อยู่ และวิธีแยก ip แย่ออกจากเว็บไซต์แย่
- ปฏิบัติ: โครงกระดูกพูลบน python และ node.js
- ข้อผิดพลาดทั่วไป: สิ่งที่ไม่ควรทำ
- เครื่องมือและทรัพยากรสำหรับการ implement
- กรณีศึกษาและผลลัพธ์การใช้งาน
- คำถามที่พบบ่อย
- บทสรุปและขั้นตอนถัดไป
ลองนึกภาพตามนะ ศุกร์เย็นแล้ว พาร์เซอร์หรือระบบจัดการบัญชีของคุณเพิ่งปล่อยลงโปรดักชั่น ไม่กี่นาทีแรกทุกอย่างลื่นไหล แต่แล้วความประหลาดก็เริ่มเกิดขึ้น: คำขอบางส่วนล้มเหลว, แคปชาโผล่มาที่นี่ที่นั่น, บัญชีบางบัญชีอยู่ดีๆ ก็ขอให้ยืนยันตัวตน, และล็อกกลายเป็นเรื่องเละเทะเต็มไปด้วย 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 ที่เป็นรูปธรรมเป็นขั้นตอน
- ห่อหุ้มการทำงานกับที่อยู่ปัจจุบันในอินเทอร์เฟซ acquire และ release โดยยังไม่เปลี่ยนอะไรภายใน นี่จะเตรียมพื้น
- เพิ่ม passive health-check และกักกันง่ายๆ ในหน่วยความจำ แค่นี้คุณก็จะรู้สึกถึงความเสถียรที่เพิ่มขึ้น
- นำกลยุทธ์การเลือกที่ต้องการเข้ามา ถ้าทำงานกับบัญชี ให้วาง sticky ผ่าน hash ตามคีย์ตั้งแต่แรก
- เชื่อมต่อ active probe และเวลาพักแบบ exponential พร้อมขีดจำกัดป้องกัน
- ย้ายสถานะไปยังที่เก็บส่วนกลางทันทีที่มี worker ตัวที่สอง วางอะตอมมิกและ TTL
- ตั้งค่าเมตริกและ dashboard โดยเฉพาะอย่างยิ่งการตัดที่อยู่-แพลตฟอร์ม
- ปรับแต่ง hysteresis และเกณฑ์ตามข้อมูลจริงของคุณ – ไม่มีตัวเลขสากล มีแต่การสังเกตของคุณเท่านั้น
อย่าพยายามสร้างทุกอย่างพร้อมกัน ให้แต่ละเลเยอร์ได้พิสูจน์ตัวเองภายใต้โหลด, เก็บเมตริก, สรุปผล, และก้าวต่อไป พูลพร็อกซีที่เติบโตเต็มที่ไม่ใช่การสร้างครั้งเดียว แต่เป็นระบบที่มีชีวิตที่คุณปรับแต่งไปพร้อมกับการเติบโตของงาน แต่แม้แต่ก้าวแรกจากคำแนะนำนี้ก็จะเปลี่ยนลิสต์ที่อยู่ที่เปราะบางให้เป็นรากฐานที่มั่นคงของแอปพลิเคชันของคุณ และนั่น, คุณคงเห็นด้วย, คุ้มค่ากับความพยายาม