บริษัทสตาร์ทอัพหลายแห่งประสบปัญหานี้ โดยมีผู้ใช้ใหม่จำนวนมากลงทะเบียนทุกวัน และทีมพัฒนาก็พยายามดิ้นรนเพื่อให้บริการดำเนินต่อไปได้
ถือเป็นปัญหาที่ดี แต่มีข้อมูลบนเว็บที่ชัดเจนเพียงเล็กน้อยเกี่ยวกับวิธีปรับขนาดแอปพลิเคชันเว็บอย่างระมัดระวังจากผู้ใช้เพียงแสนคนไปเป็นผู้ใช้หลายแสนราย โดยทั่วไปจะมีวิธีแก้ปัญหาอัคคีภัยหรือวิธีแก้ปัญหาคอขวด (และมักมีทั้งสองอย่าง) ดังนั้น ผู้คนจึงใช้เทคนิคที่ค่อนข้างซ้ำซากเพื่อขยายโปรเจ็กต์สมัครเล่นให้กลายเป็นเรื่องจริงจัง
ลองกรองข้อมูลและเขียนสูตรพื้นฐานดู เราจะขยายไซต์แบ่งปันรูปภาพใหม่ของเรา Graminsta ทีละขั้นตอนจากผู้ใช้ 1 คนเป็น 100 คน
มาจดบันทึกว่าต้องดำเนินการอะไรบ้างเมื่อผู้ชมเพิ่มขึ้นเป็น 10, 100, 1000, 10 และ 000 คน
ผู้ใช้ 1 คน: 1 เครื่อง
เกือบทุกแอปพลิเคชัน ไม่ว่าจะเป็นเว็บไซต์หรือแอปพลิเคชันบนมือถือ มีองค์ประกอบหลักสามประการ:
- API
- ฐานข้อมูล
- ลูกค้า (แอปพลิเคชันมือถือหรือเว็บไซต์)
ฐานข้อมูลจัดเก็บข้อมูลถาวร API ตอบสนองคำขอไปยังและรอบๆ ข้อมูลนี้ ลูกค้าส่งข้อมูลไปยังผู้ใช้
ฉันสรุปได้ว่าการปรับขนาดแอปพลิเคชันจะง่ายกว่ามาก หากจากมุมมองทางสถาปัตยกรรม ไคลเอ็นต์และเอนทิตี API แยกจากกันโดยสิ้นเชิง
เมื่อเราเริ่มสร้างแอปพลิเคชันเป็นครั้งแรก ทั้งสามองค์ประกอบสามารถทำงานบนเซิร์ฟเวอร์เดียวกันได้ สิ่งนี้จะคล้ายกับสภาพแวดล้อมการพัฒนาของเราในแง่หนึ่ง กล่าวคือ วิศวกรคนหนึ่งรันฐานข้อมูล, API และไคลเอนต์บนเครื่องเดียวกัน
ตามทฤษฎีแล้ว เราสามารถปรับใช้ในระบบคลาวด์บน DigitalOcean Droplet หรือ AWS EC2 instance เดียวได้ ดังที่แสดงด้านล่าง:
ดังที่กล่าวไปแล้ว หากมีผู้ใช้มากกว่าหนึ่งรายบนไซต์ การอุทิศเลเยอร์ฐานข้อมูลมักจะสมเหตุสมผลเสมอไป
ผู้ใช้ 10 ราย: ย้ายฐานข้อมูลไปยังระดับที่แยกจากกัน
การแยกฐานข้อมูลออกเป็นบริการที่ได้รับการจัดการ เช่น Amazon RDS หรือ Digital Ocean Managed Database จะให้บริการเราอย่างดีเป็นเวลานาน มีราคาแพงกว่าการโฮสต์ด้วยตนเองบนเครื่องเดียวหรือ EC2 instance เล็กน้อย แต่ด้วยบริการเหล่านี้ คุณจะได้รับส่วนขยายที่มีประโยชน์มากมายซึ่งจะมีประโยชน์ในอนาคต เช่น การสำรองข้อมูลหลายภูมิภาค การจำลองการอ่าน อัตโนมัติ การสำรองข้อมูลและอื่น ๆ
นี่คือลักษณะของระบบตอนนี้:
ผู้ใช้ 100 ราย: ย้ายไคลเอนต์ไปยังระดับที่แยกจากกัน
โชคดีที่ผู้ใช้กลุ่มแรกของเราชอบแอปพลิเคชันของเรามาก การรับส่งข้อมูลเริ่มมีเสถียรภาพมากขึ้น ดังนั้นจึงถึงเวลาที่ต้องย้ายลูกค้าไปยังระดับที่แยกจากกัน ก็ควรสังเกตว่า การแยกทาง เอนทิตีเป็นส่วนสำคัญในการสร้างแอปพลิเคชันที่ปรับขนาดได้ เนื่องจากส่วนหนึ่งของระบบได้รับการรับส่งข้อมูลมากขึ้น เราสามารถแบ่งพาร์ติชันเพื่อควบคุมวิธีที่บริการจะปรับขนาดตามรูปแบบการรับส่งข้อมูลเฉพาะได้
นี่คือเหตุผลที่ฉันชอบคิดว่าไคลเอนต์แยกจาก API สิ่งนี้ทำให้ง่ายมากที่จะคิดเกี่ยวกับการพัฒนาสำหรับหลายแพลตฟอร์ม: เว็บ, เว็บบนมือถือ, iOS, Android, แอปพลิเคชันเดสก์ท็อป, บริการของบุคคลที่สาม ฯลฯ ทั้งหมดเป็นเพียงลูกค้าที่ใช้ API เดียวกัน
ตัวอย่างเช่น ขณะนี้ผู้ใช้ของเรามักขอให้เผยแพร่แอปพลิเคชันบนมือถือ หากคุณแยกไคลเอ็นต์และเอนทิตี API สิ่งนี้จะง่ายขึ้น
นี่คือลักษณะของระบบดังกล่าว:
ผู้ใช้ 1000 ราย: เพิ่มโหลดบาลานเซอร์
สิ่งต่าง ๆ กำลังมองหา ผู้ใช้ Graminsta กำลังอัปโหลดรูปภาพมากขึ้นเรื่อยๆ จำนวนการลงทะเบียนก็เพิ่มขึ้นเช่นกัน เซิร์ฟเวอร์ API เดี่ยวของเรากำลังประสบปัญหาในการติดตามการรับส่งข้อมูลทั้งหมด ต้องการเหล็กเพิ่ม!
โหลดบาลานเซอร์เป็นแนวคิดที่ทรงพลังมาก แนวคิดหลักคือเราวางโหลดบาลานเซอร์ไว้ด้านหน้า API และจะกระจายการรับส่งข้อมูลไปยังอินสแตนซ์บริการแต่ละรายการ นี่คือวิธีที่เราปรับขนาดในแนวนอน ซึ่งหมายความว่าเราจะเพิ่มเซิร์ฟเวอร์ด้วยรหัสเดียวกัน ซึ่งจะทำให้จำนวนคำขอที่เราสามารถดำเนินการเพิ่มมากขึ้น
เราจะวางโหลดบาลานเซอร์แยกต่างหากไว้ด้านหน้าเว็บไคลเอ็นต์และด้านหน้า API ซึ่งหมายความว่าคุณสามารถเรียกใช้หลายอินสแตนซ์ที่ใช้โค้ด API และโค้ดไคลเอ็นต์ของเว็บได้ โหลดบาลานเซอร์จะส่งคำขอโดยตรงไปยังเซิร์ฟเวอร์ที่มีการโหลดน้อย
ที่นี่เราได้รับข้อได้เปรียบที่สำคัญอีกประการหนึ่ง - ความซ้ำซ้อน เมื่ออินสแตนซ์หนึ่งล้มเหลว (อาจโอเวอร์โหลดหรือล่ม) เราจะเหลืออินสแตนซ์อื่นที่ยังคงตอบสนองต่อคำขอที่เข้ามา หากมีอินสแตนซ์เพียงตัวเดียวที่ทำงาน ในกรณีที่เกิดความล้มเหลว ระบบทั้งหมดก็จะเสียหาย
โหลดบาลานเซอร์ยังมีการปรับขนาดอัตโนมัติอีกด้วย เราสามารถกำหนดค่าให้เพิ่มจำนวนอินสแตนซ์ก่อนโหลดสูงสุด และลดน้อยลงเมื่อผู้ใช้ทั้งหมดอยู่ในโหมดสลีป
ด้วยโหลดบาลานเซอร์ ระดับ API สามารถปรับขนาดได้เกือบไม่มีกำหนด เพียงเพิ่มอินสแตนซ์ใหม่เมื่อจำนวนคำขอเพิ่มขึ้น
บันทึก. ขณะนี้ระบบของเราคล้ายกันมากกับสิ่งที่บริษัท PaaS เช่น Heroku หรือ Elastic Beanstalk บน AWS เสนอให้ทันที (ซึ่งเป็นเหตุผลว่าทำไมจึงได้รับความนิยมมาก) Heroku วางฐานข้อมูลบนโฮสต์ที่แยกต่างหาก จัดการโหลดบาลานเซอร์ที่ปรับขนาดอัตโนมัติ และอนุญาตให้คุณโฮสต์เว็บไคลเอ็นต์แยกต่างหากจาก API นี่เป็นเหตุผลที่ดีในการใช้ Heroku สำหรับโครงการระยะเริ่มต้นหรือสตาร์ทอัพ คุณจะได้รับบริการพื้นฐานทั้งหมดทันที
ผู้ใช้ 10 ราย: CDN
บางทีเราควรจะทำสิ่งนี้ตั้งแต่แรกเริ่ม การประมวลผลคำขอและการยอมรับภาพถ่ายใหม่เริ่มทำให้เซิร์ฟเวอร์ของเราเครียดมากเกินไป
ในขั้นตอนนี้ คุณต้องใช้บริการคลาวด์เพื่อจัดเก็บเนื้อหาคงที่ เช่น รูปภาพ วิดีโอ และอื่นๆ อีกมากมาย (AWS S3 หรือ Digital Ocean Spaces) โดยทั่วไป API ของเราควรหลีกเลี่ยงการจัดการสิ่งต่างๆ เช่น การแสดงรูปภาพและการอัพโหลดรูปภาพไปยังเซิร์ฟเวอร์
ข้อดีอีกประการหนึ่งของโฮสติ้งบนคลาวด์ก็คือ CDN (AWS เรียกส่วนเสริมนี้ว่า Cloudfront แต่ผู้ให้บริการพื้นที่เก็บข้อมูลบนคลาวด์หลายรายเสนอบริการดังกล่าวทันที) CDN จะแคชรูปภาพของเราในศูนย์ข้อมูลต่างๆ ทั่วโลกโดยอัตโนมัติ
แม้ว่าศูนย์ข้อมูลหลักของเราอาจตั้งอยู่ในโอไฮโอ แต่หากมีผู้ขอรูปภาพจากญี่ปุ่น ผู้ให้บริการคลาวด์จะทำสำเนาและจัดเก็บไว้ในศูนย์ข้อมูลของญี่ปุ่น คนต่อไปที่ขอภาพนี้ในญี่ปุ่นจะได้รับเร็วกว่ามาก นี่เป็นสิ่งสำคัญเมื่อเราทำงานกับไฟล์ขนาดใหญ่ เช่น รูปภาพหรือวิดีโอ ที่ใช้เวลานานในการดาวน์โหลดและส่งข้อมูลทั่วโลก
ผู้ใช้ 100 ราย: ปรับขนาดชั้นข้อมูล
CDN ช่วยได้มาก: ปริมาณการใช้ข้อมูลเพิ่มขึ้นด้วยความเร็วสูงสุด Mavid Mobrick บล็อกเกอร์วิดีโอชื่อดังเพิ่งลงทะเบียนกับเราและโพสต์ "เรื่องราว" ของเขาตามที่พวกเขาพูด ต้องขอบคุณโหลดบาลานเซอร์ที่ทำให้การใช้งาน CPU และหน่วยความจำบนเซิร์ฟเวอร์ API อยู่ในระดับต่ำ (อินสแตนซ์ API สิบรายการทำงานอยู่) แต่เราเริ่มได้รับการหมดเวลาจำนวนมากสำหรับคำขอ... ความล่าช้าเหล่านี้มาจากไหน
เมื่อเจาะลึกตัวชี้วัดเล็กน้อย เราจะเห็นว่า CPU บนเซิร์ฟเวอร์ฐานข้อมูลมีการโหลด 80-90% เรามาถึงขีดจำกัดแล้ว
การปรับขนาดชั้นข้อมูลอาจเป็นส่วนที่ยากที่สุดของสมการ เซิร์ฟเวอร์ API ให้บริการคำขอแบบไม่เก็บสถานะ ดังนั้นเราจึงเพิ่มอินสแตนซ์ API เพิ่มเติม จมูก ส่วนใหญ่ ฐานข้อมูลไม่สามารถทำเช่นนี้ได้ เราจะพูดถึงระบบการจัดการฐานข้อมูลเชิงสัมพันธ์ยอดนิยม (PostgreSQL, MySQL ฯลฯ)
เก็บเอาไว้
วิธีที่ง่ายที่สุดวิธีหนึ่งในการเพิ่มประสิทธิภาพฐานข้อมูลของเราคือการแนะนำส่วนประกอบใหม่: เลเยอร์แคช วิธีการแคชที่พบบ่อยที่สุดคือการจัดเก็บบันทึกคีย์-ค่าในหน่วยความจำ เช่น Redis หรือ Memcached คลาวด์ส่วนใหญ่มีบริการเวอร์ชันที่ได้รับการจัดการเหล่านี้: Elasticache บน AWS และ Memorystore บน Google Cloud
แคชมีประโยชน์เมื่อบริการทำการเรียกฐานข้อมูลซ้ำหลายครั้งเพื่อดึงข้อมูลเดียวกัน โดยพื้นฐานแล้ว เราเข้าถึงฐานข้อมูลเพียงครั้งเดียว เก็บข้อมูลไว้ในแคช และไม่ต้องแตะต้องมันอีก
ตัวอย่างเช่น ในบริการ Graminsta ของเรา ทุกครั้งที่มีคนไปที่หน้าโปรไฟล์ของดารา Mobrik เซิร์ฟเวอร์ API จะสอบถามฐานข้อมูลจากโปรไฟล์ของเขา สิ่งนี้เกิดขึ้นครั้งแล้วครั้งเล่า เนื่องจากข้อมูลโปรไฟล์ของ Mobrik จะไม่เปลี่ยนแปลงไปในแต่ละคำขอ จึงเหมาะสำหรับการแคชเป็นอย่างยิ่ง
เราจะแคชผลลัพธ์จากฐานข้อมูลใน Redis ตามคีย์ user:id
โดยมีระยะเวลาใช้งานได้ 30 วินาที ตอนนี้ เมื่อมีคนไปที่โปรไฟล์ของ Mobrik เราจะตรวจสอบ Redis ก่อน และหากมีข้อมูลอยู่ เราก็จะถ่ายโอนข้อมูลจาก Redis โดยตรง ขณะนี้คำขอไปยังโปรไฟล์ที่ได้รับความนิยมสูงสุดบนไซต์ไม่ได้โหลดฐานข้อมูลของเราเลย
ข้อดีอีกประการหนึ่งของบริการแคชส่วนใหญ่ก็คือ ปรับขนาดได้ง่ายกว่าเซิร์ฟเวอร์ฐานข้อมูล Redis มีโหมด Redis Cluster ในตัว คล้ายกับโหลดบาลานเซอร์
แอปพลิเคชันขนาดใหญ่เกือบทั้งหมดใช้แคช ซึ่งเป็นส่วนสำคัญของ API ที่รวดเร็ว การประมวลผลคำค้นหาที่เร็วขึ้นและโค้ดที่มีประสิทธิภาพมากขึ้นล้วนมีความสำคัญ แต่หากไม่มีแคช แทบจะเป็นไปไม่ได้เลยที่จะขยายบริการไปยังผู้ใช้หลายล้านคน
อ่านแบบจำลอง
เมื่อจำนวนการสืบค้นไปยังฐานข้อมูลเพิ่มขึ้นอย่างมาก อีกหนึ่งสิ่งที่เราทำได้คือการเพิ่มแบบจำลองการอ่านในระบบการจัดการฐานข้อมูล ด้วยบริการที่มีการจัดการที่อธิบายไว้ข้างต้น สามารถทำได้ในคลิกเดียว แบบจำลองการอ่านจะยังคงอยู่ในฐานข้อมูลหลักและพร้อมใช้งานสำหรับคำสั่ง SELECT
นี่คือระบบของเราตอนนี้:
ขั้นตอนถัดไป
เนื่องจากแอปพลิเคชันยังคงขยายขนาดต่อไป เราจะแยกบริการต่อไปเพื่อปรับขนาดแยกกัน ตัวอย่างเช่น หากเราเริ่มใช้ Websockets ก็สมเหตุสมผลที่จะดึงโค้ดประมวลผล Websockets เข้าสู่บริการแยกต่างหาก เราสามารถวางไว้บนอินสแตนซ์ใหม่ด้านหลังโหลดบาลานเซอร์ของเราเอง ซึ่งสามารถปรับขนาดขึ้นและลงได้ตามการเชื่อมต่อ Websockets แบบเปิด โดยไม่คำนึงถึงจำนวนคำขอ HTTP
นอกจากนี้เรายังจะต่อสู้กับข้อจำกัดในระดับฐานข้อมูลต่อไป มาถึงขั้นตอนนี้แล้วจึงถึงเวลาศึกษาการแบ่งพาร์ติชันและการแบ่งส่วนฐานข้อมูล ทั้งสองวิธีต้องการค่าใช้จ่ายเพิ่มเติม แต่ช่วยให้คุณสามารถปรับขนาดฐานข้อมูลได้เกือบจะไม่มีกำหนด
นอกจากนี้เรายังต้องการติดตั้งบริการตรวจสอบและวิเคราะห์เช่น New Relic หรือ Datadog สิ่งนี้จะช่วยคุณระบุการสืบค้นที่ช้าและเข้าใจว่าจุดใดจำเป็นต้องปรับปรุง เมื่อเราขยายขนาด เราต้องการมุ่งเน้นไปที่การค้นหาปัญหาคอขวดและขจัดปัญหาเหล่านั้น ซึ่งมักใช้แนวคิดบางส่วนจากส่วนก่อนหน้า
แหล่งที่มา
โพสต์นี้ได้แรงบันดาลใจจากหนึ่งใน
เชิงอรรถ
- แม้ว่าจะคล้ายกันในแง่ของการกระจายโหลดในหลายอินสแตนซ์ แต่การใช้งานพื้นฐานของคลัสเตอร์ Redis นั้นแตกต่างจากโหลดบาลานเซอร์อย่างมาก
[กลับ]
ที่มา: will.com