อัลกอริทึมอิงชีวิตจริง

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

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

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

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

ความเหมือนจะดีของคิว

คิวเป็นเครื่องมือทรงพลังสำหรับการสร้างระบบอะซิงโครนัสที่เชื่อถือได้ คิวทำให้ระบบหนึ่งยอมรับข้อความจากระบบอื่น และคงข้อความไว้จนกว่าจะถูกประมวลผลอย่างสมบูรณ์ แม้จะสัญญาณขาดหาย เซิร์ฟเวอร์ล้มเหลว หรือพบปัญหาเรื่องระบบที่ต้องพึ่งพาก็ตาม แทนที่จะทิ้งข้อความเมื่อเกิดความล้มเหลว คิวจะขับดันข้อความใหม่จนกว่าจะประมวลผลสำเร็จ ท้ายที่สุด คิวจะเพิ่มความคงทนและความพร้อมใช้งานของระบบ โดยแลกกับเวลาแฝงที่เพิ่มขึ้นเป็นครั้งคราวเนื่องจากการลองใหม่
 
ที่ Amazon เราสร้างระบบอะซิงโครนัสจำนวนมากที่ใช้ประโยชน์จากคิว บางระบบประมวลผลเวิร์กโฟลว์ที่อาจใช้เวลานานและเกี่ยวข้องกับการเคลื่อนย้ายของไปทั่วโลก เช่น การจัดส่งตามคำสั่งซื้อที่ amazon.com ระบบอื่นๆ จะประสานขั้นตอนต่างๆ ที่อาจใช้เวลามาก เช่น Amazon RDS ร้องขอ EC2 instance รอให้เปิดตัว จากนั้นกำหนดค่าฐานข้อมูลสำหรับคุณ ระบบอื่นใช้ประโยชน์จากการสร้างชุด เช่น ระบบที่เกี่ยวข้องกับการรับเข้าตัววัดและบันทึก CloudWatch จะดึงข้อมูลเป็นจำนวนมาก จากนั้นรวบรวมและ "ทำให้แบน" ลงในกลุ่มข้อมูล
 
แม้ว่าการเห็นประโยชน์ของคิวสำหรับการประมวลผลข้อความแบบอะซิงโครนัสนั้นจะเป็นเรื่องง่าย แต่ความเสี่ยงของการใช้คิวนั้นเจาะจงได้ยากกว่ามาก ในช่วงหลายปีที่ผ่านมาเราพบว่าการจัดคิวเพื่อปรับปรุงความพร้อมใช้งานนั้นอาจส่งผลเสียแทนได้ อันที่จริง อาจใช้เวลากู้คืนเพิ่มขึ้นมากหลังจากสัญญาณขาดหายด้วยซ้ำไป
 
ในระบบตามคิว เมื่อการประมวลผลหยุดลงแต่ข้อความยังมาเรื่อยๆ กองข้อความอาจสะสมจนค้างอยู่เป็นจำนวนมาก ทำให้ต้องใช้เวลาประมวลผลนานขึ้น งานอาจเสร็จช้าเกินไปจนผลลัพธ์ไม่เกิดประโยชน์แล้ว ทำให้เกิดความไม่พร้อมใช้งานทั้งที่การจัดคิวนั้นมีไว้เพื่อป้องกันปัญหานี้
 
กล่าวอีกแบบคือ ระบบตามคิวมีสองโหมดการดำเนินงาน หรือพฤติกรรมแบบทวิฐานนิยม กล่าวอีกแบบคือ ระบบที่ใช้คิวมีสองโหมดการดำเนินงานหรือพฤติกรรมแบบทวิฐานนิยม แต่หากความล้มเหลวหรือรูปแบบการโหลดที่ไม่คาดคิดทำให้อัตราการมาถึงมากเกินกว่าอัตราการประมวลผล ก็จะพลิกเข้าสู่โหมดการดำเนินงานที่เลวร้ายกว่าเดิม ในโหมดนี้ เวลาแฝงตั้งแต่ต้นจนจบจะสูงขึ้นเรื่อยๆ และอาจต้องใช้เวลานานในการทำงานที่ค้างอยู่กว่าจะกลับสู่โหมดเร็วได้

ระบบตามคิว

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

AWS Lambda จะให้คุณได้อัปโหลดโค้ดฟังก์ชันของคุณ จากนั้นเรียกใช้ฟังก์ชันได้สองแบบ:

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

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

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

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

ความล้มเหลวในระบบอะซิงโครนัส

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

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

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

เราวัดความพร้อมใช้งานและเวลาแฝงอย่างไร

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

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

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

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

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

ใน AWS IoT เราใช้ตัววัดอื่นกับบริการ: AgeOfFirstAttempt การวัดนี้จะบันทึกเวลาปัจจุบันลบด้วยเวลาที่เพิ่มข้อความลงในคิว แต่เฉพาะเมื่อนี่เป็นครั้งแรกที่ AWS IoT พยายามส่งข้อความไปยังอุปกรณ์เท่านั้น วิธีนี้เมื่อสำรองข้อมูลอุปกรณ์ เราจะมีตัววัดที่หมดจด ไม่ปนเปื้อนด้วยอุปกรณ์ที่พยายามส่งข้อความหรือเพิ่มลงในคิว เพื่อให้ตัววัดหมดจดยิ่งขึ้น เราจึงปล่อยตัววัดที่สองขึ้นมา – AgeOfFirstSubscriberFirstAttempt ในระบบ PubSub เช่น AWS IoT ไม่มีข้อจำกัดในทางปฏิบัติว่ามีกี่อุปกรณ์หรือกี่แอปพลิเคชันที่สามารถสมัครใช้งานหัวข้อใดหัวข้อหนึ่งได้ ดังนั้นเวลาแฝงจึงสูงกว่าเมื่อส่งข้อความไปยังอุปกรณ์หนึ่งล้านเครื่องถ้าเทียบกับเมื่อส่งไปยังอุปกรณ์เครื่องเดียว เพื่อให้เรามีตัววัดที่เสถียร เราจึงปล่อยตัววัดแบบจับเวลาในความพยายามครั้งแรกในการเผยแพร่ข้อความไปยังสมาชิกคนแรกในหัวข้อนั้น จากนั้นเรามีตัววัดอื่นๆ เพื่อวัดความคืบหน้าของระบบในการเผยแพร่ข้อความที่เหลือ

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

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

งานค้างในระบบอะซิงโครนัสแบบมัลติเทนแนนท์

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

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

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

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

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

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

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

กลยุทธ์ของ Amazon สำหรับการสร้างระบบอะซิงโครนัสแบบมัลติเทนแนนท์ที่มีความยืดหยุ่น

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

การแยกปริมาณงานออกเป็นคิวแยกต่างหาก

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

สลับสับเป็นส่วนเล็กส่วนน้อย

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

การกันปริมาณการใช้งานส่วนเกินออกไปยังคิวแยกต่างหาก

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

การกันปริมาณการใช้งานเก่าออกไปยังคิวแยกต่างหาก

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

การทิ้งข้อความเก่า (ข้อความ Time to Live)

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

การจำกัดเธรด (และทรัพยากรอื่นๆ) ต่อปริมาณงาน

เช่นเดียวกับในบริการซิงโครนัสส่วนใหญ่ของเรา เราออกแบบระบบอะซิงโครนัสเพื่อป้องกันไม่ให้ปริมาณงานหนึ่งใช้มากกว่าส่วนแบ่งเธรดที่เป็นธรรม แง่มุมหนึ่งของ AWS IoT ที่เรายังไม่ได้พูดถึงคือกลไกจัดการกฎ ลูกค้าสามารถกำหนดค่า AWS IoT เพื่อกำหนดเส้นทางข้อความจากอุปกรณ์ของตนไปยังคลัสเตอร์ Amazon Elasticsearch ของลูกค้า, Kinesis Stream และอื่นๆ หากเวลาแฝงของทรัพยากรที่ลูกค้าเป็นเจ้าของนั้นช้า แต่อัตราข้อความขาเข้ายังคงไม่เปลี่ยนแปลง จำนวนของภาวะพร้อมกันในระบบจะเพิ่มขึ้น และเพราะจำนวนของภาวะพร้อมกันที่ระบบสามารถจัดการได้ถูกจำกัด อยู่ตลอดเวลา กลไกจัดการกฎจะป้องกันไม่ให้ปริมาณงานใดๆ ใช้ทรัพยากรเกินกว่าส่วนแบ่งที่เป็นธรรมที่เกี่ยวข้องกับภาวะพร้อมกัน

อำนาจที่ส่งผลอยู่นี้อธิบายได้โดยกฎของลิตเติล ซึ่งระบุว่าภาวะพร้อมกันในระบบเท่ากับอัตราการมาถึงคูณด้วยเวลาแฝงเฉลี่ยของแต่ละคำขอ เช่น หากเซิร์ฟเวอร์ประมวลผล 100 ข้อความ/วินาทีที่ 100 มิลลิวินาทีโดยเฉลี่ย เซิร์ฟเวอร์จะใช้ 10 เธรดโดยเฉลี่ย หากเวลาแฝงพุ่งพรวดถึง 10 วินาที ก็จะใช้ 1,000 เธรด (โดยเฉลี่ย ดังนั้นในทางปฏิบัติอาจมากกว่านี้) ซึ่งทำให้เธรดพูลหมดได้ง่ายๆ

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

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

การดันกลับแบบอัพสตรีม

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

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

การดันกลับไม่เหมาะสำหรับทุกระบบของ Amazon เช่น ในระบบที่ประมวลผลคำสั่งซื้อสำหรับ amazon.com เรามักจะยอมรับคำสั่งซื้อแม้ว่าจะมีงานค้างเพิ่มขึ้น แทนที่จะขัดขวางไม่ให้รับคำสั่งซื้อใหม่ แต่แน่นอนว่าเรื่องนี้มาพร้อมกับการจัดลำดับความสำคัญมากมายอยู่เบื้องหลังเพื่อรับมือกับคำสั่งซื้อด่วนที่สุดก่อน

การใช้คิวหน่วงเวลาเพื่อเลื่อนงานออกไปทีหลัง

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

หลีกเลี่ยงการมีข้อความที่ค้างอยู่มากเกินไป

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

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

การใช้คิวที่ทำไม่สำเร็จสำหรับข้อความที่ไม่สามารถประมวลผลได้

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

การรับประกันบัฟเฟอร์เพิ่มเติมในเธรดที่หยั่งสัญญาณต่อปริมาณงาน

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

การส่งสัญญาณเชื่อมต่อข้อความที่ดำเนินการมานานอีกครั้ง

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

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

วางแผนสำหรับการดีบักข้ามโฮสต์

การทำความเข้าใจความล้มเหลวในระบบแบบกระจายนั้นเป็นเรื่องยากอยู่แล้ว บทความที่เกี่ยวข้องเกี่ยวกับการใช้เครื่องมือวัดจะอธิบายแนวทางของเราหลายประการสำหรับการใช้เครื่องมือวัดระบบอะซิงโครนัส ตั้งแต่การบันทึกความลึกของคิวเป็นระยะจนถึงการแพร่กระจาย "ID การติดตาม" และการรวมเข้ากับ X-Ray หรือเมื่อระบบของเรามีเวิร์กโฟลว์แบบอะซิงโครนัสที่ซับซ้อนเกินกว่าคิว SQS ธรรมดาๆ เรามักจะใช้บริการเวิร์กโฟลว์แบบอะซิงโครนัสอื่น เช่น Step Functions ซึ่งให้ทำให้มองเห็นเวิร์กโฟลว์และทำให้การดีบักแบบกระจายง่ายขึ้น

สรุป

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

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

อ่านเพิ่มเติม

เกี่ยวกับผู้เขียน

David Yanacek เป็นวิศวกรหลักอาวุโสที่ทำงานกับ AWS Lambda David เป็นนักพัฒนาซอฟต์แวร์ที่ Amazon ตั้งแต่ปี 2006 โดยก่อนหน้านี้ทำงานเกี่ยวกับ Amazon DynamoDB และ AWS IoT รวมถึงเฟรมเวิร์กบริการเว็บภายในและระบบปฏิบัติการฟลีตอัตโนมัติ กิจกรรมการทำงานอย่างหนึ่งที่ David โปรดปรานคือการวิเคราะห์บันทึกและตรวจสอบตัวชี้วัดการดำเนินงานอย่างใกล้ชิดเพื่อหาวิธีที่จะทำให้ระบบทำงานได้ราบรื่นยิ่งๆ ขึ้นไป

การเลือกผู้นำในระบบแบบกระจาย การอินสตรูเมนต์ระบบแบบกระจายสำหรับความสามารถในการมองเห็นเชิงการดำเนินงาน