Braze ใช้ประโยชน์จากทับทิมในระดับต่างๆ ได้อย่างไร

เผยแพร่แล้ว: 2022-08-18

หากคุณเป็นวิศวกรที่อ่าน Hacker News, Developer Twitter หรือแหล่งข้อมูลอื่นๆ ที่คล้ายคลึงกัน คุณมักจะพบบทความนับพันที่มีชื่ออย่าง “Speed ​​of Rust vs C”, “What Makes Node. js เร็วกว่า Java?” หรือ “ทำไมคุณจึงควรใช้ Golang และวิธีเริ่มต้นใช้งาน” บทความเหล่านี้มักทำให้กรณีที่มีภาษาเฉพาะนี้ที่เป็นตัวเลือกที่ชัดเจนสำหรับความสามารถในการปรับขนาดหรือความเร็ว—และสิ่งเดียวที่คุณต้องทำคือยอมรับมัน

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

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

ที่ Braze เราดำเนินงานในระดับโลกอันยิ่งใหญ่ และใช่ เราใช้ Ruby และ Rails เป็นเครื่องมือหลักสองอย่างในการทำสิ่งนี้ อย่างไรก็ตาม ไม่มีค่าคอนฟิกูเรชัน “global_scale = true” ที่ทำให้ทุกอย่างเป็นไปได้—เป็นผลมาจากสถาปัตยกรรมที่คิดมาอย่างดีซึ่งครอบคลุมส่วนลึกภายในแอปพลิเคชันตลอดทางจนถึงการปรับใช้โทโพโลยี วิศวกรของ Braze คอยตรวจสอบปัญหาคอขวดของสเกลอย่างต่อเนื่องและหาวิธีทำให้ระบบของเราเร็วขึ้น และคำตอบก็มักจะไม่ใช่ “การย้ายออกจาก Ruby”: เกือบจะแน่นอนว่าจะเป็นการเปลี่ยนแปลงสถาปัตยกรรม

มาดูกันว่า Braze ใช้ประโยชน์จากสถาปัตยกรรมที่รอบคอบเพื่อแก้ปัญหาด้านความเร็วและขนาดที่ใหญ่ระดับโลกได้อย่างไร และที่ที่ Ruby และ Rails เข้ากันได้ (แต่ไม่เป็นเช่นนั้น)!

พลังของสถาปัตยกรรมที่ดีที่สุดในระดับเดียวกัน

คำขอทางเว็บอย่างง่าย

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

  1. เริ่มต้นด้วยตัวแก้ไข DNS ของไคลเอ็นต์ (โดยปกติคือ ISP ของไคลเอ็นต์) เพื่อค้นหาที่อยู่ IP ที่จะไปที่ ตามโดเมนใน URL ของเว็บไซต์ของคุณ

  2. เมื่อไคลเอนต์มีที่อยู่ IP พวกเขาจะส่งคำขอไปยังเราเตอร์เกตเวย์ ซึ่งจะส่งไปยังเราเตอร์ "next hop" (ซึ่งอาจเกิดขึ้นหลายครั้ง) จนกว่าคำขอจะไปถึงที่อยู่ IP ปลายทาง

  3. จากนั้น ระบบปฏิบัติการบนเซิร์ฟเวอร์ที่ได้รับคำขอจะจัดการรายละเอียดเครือข่ายและแจ้งกระบวนการรอของเว็บเซิร์ฟเวอร์ว่าได้รับคำขอขาเข้าบนซ็อกเก็ต/พอร์ตที่กำลังรับฟังอยู่

  4. เว็บเซิร์ฟเวอร์จะเขียนการตอบกลับ (ทรัพยากรที่ร้องขอ อาจเป็น index.html) ไปยังซ็อกเก็ตนั้น ซึ่งจะเดินทางย้อนกลับผ่านเราเตอร์กลับไปยังไคลเอนต์

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

ระบบแรกที่รองรับมาตราส่วน

โดยทั่วไป DNS และเนมเซิร์ฟเวอร์ไม่ต้องการความสนใจมากนักในกรณีส่วนใหญ่ เซิร์ฟเวอร์ชื่อโดเมนระดับบนสุดของคุณอาจมีรายการสองสามรายการเพื่อจับคู่ “yourwebsite.com” กับเนมเซิร์ฟเวอร์สำหรับโดเมนของคุณ และหากคุณใช้บริการเช่น Amazon Route 53 หรือ Azure DNS พวกเขาจะจัดการชื่อ เซิร์ฟเวอร์สำหรับโดเมนของคุณ (เช่น การจัดการ A, CNAME หรือระเบียนประเภทอื่นๆ) คุณมักจะไม่ต้องคิดเกี่ยวกับการปรับขนาดส่วนนี้ เนื่องจากระบบที่คุณใช้อยู่จะจัดการส่วนนี้โดยอัตโนมัติ

อย่างไรก็ตาม ส่วนการกำหนดเส้นทางของโฟลว์นั้นน่าสนใจ มีอัลกอริธึมการกำหนดเส้นทางที่แตกต่างกันสองสามอย่าง เช่น Open Shortest Path First หรือ Routing Information Protocol ซึ่งทั้งหมดได้รับการออกแบบมาเพื่อค้นหาเส้นทางที่เร็ว/สั้นที่สุดจากไคลเอนต์ไปยังเซิร์ฟเวอร์ เนื่องจากอินเทอร์เน็ตเป็นกราฟที่เชื่อมต่อขนาดยักษ์อย่างมีประสิทธิภาพ (หรืออีกทางหนึ่งคือเครือข่ายการไหล) อาจมีหลายเส้นทางที่สามารถใช้ประโยชน์ได้ โดยแต่ละเส้นทางมีต้นทุนที่สูงขึ้นหรือต่ำลง การทำงานเพื่อค้นหาเส้นทางที่เร็วที่สุดจะเป็นการห้ามปราม ดังนั้นอัลกอริธึมส่วนใหญ่จึงใช้ฮิวริสติกที่สมเหตุสมผลเพื่อให้ได้เส้นทางที่ยอมรับได้ คอมพิวเตอร์และเครือข่ายไม่น่าเชื่อถือเสมอไป ดังนั้นเราจึงพึ่งพา Fastly เพื่อปรับปรุงความสามารถของไคลเอ็นต์ในการกำหนดเส้นทางไปยังเซิร์ฟเวอร์ของเราได้รวดเร็วยิ่งขึ้น

ทำงานอย่างรวดเร็วโดยให้จุดแสดงตน (POP) ทั่วโลกด้วยการเชื่อมต่อที่รวดเร็วและเชื่อถือได้ระหว่างจุดเหล่านี้ คิดว่าพวกเขาเป็นทางหลวงระหว่างรัฐของอินเทอร์เน็ต ระเบียน A และ CNAME ของโดเมนของเราชี้ไปที่ Fastly ซึ่งทำให้คำขอของลูกค้าไปที่ทางหลวงโดยตรง จากที่นั่น Fastly สามารถกำหนดเส้นทางไปยังที่ที่ถูกต้องได้

ประตูหน้าสู่ประสาน

ตกลง คำขอของลูกค้าของเราได้ไปตามทางหลวง Fastly และอยู่ที่ประตูหน้าของแพลตฟอร์ม Braze แล้ว จะเกิดอะไรขึ้นต่อไป

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

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

เว็บเซิร์ฟเวอร์

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

เนื่องจากคำขอส่วนใหญ่ที่ส่งโดย SDK ของเราภายในแอปพลิเคชันของลูกค้าหรือผ่าน REST API ของเรานั้นไม่พร้อมกัน (กล่าวคือ เราไม่ต้องรอให้การดำเนินการเสร็จสิ้นเพื่อส่งคืนการตอบกลับเฉพาะไปยังไคลเอ็นต์) คำขอส่วนใหญ่ของเรา เซิร์ฟเวอร์ API นั้นเรียบง่ายเป็นพิเศษ โดยจะตรวจสอบโครงสร้างของคำขอ ข้อจำกัดของคีย์ API ใดๆ จากนั้นจึงส่งคำขอในคิว Redis และส่งคืนการตอบกลับ 200 รายการไปยังไคลเอ็นต์หากตรวจสอบทุกอย่างแล้ว

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

คิวงาน

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

คนงาน

นี่เป็นส่วนที่น่าสนใจที่สุดอย่างแน่นอน—เราจะทำให้พนักงานขยายขนาดได้อย่างไร

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

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

ที่เก็บข้อมูล

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

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

ออบเจ็กต์ข้อมูลอิสระ

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

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

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

ความสม่ำเสมอในที่สุด

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

เราใช้วิธีการแบบหลายเลเยอร์โดยใช้ Memcached โดยพื้นฐานแล้ว เมื่อขอเอกสารจากฐานข้อมูล ก่อนอื่นเราจะตรวจสอบกระบวนการ Memcached ในเครื่องที่มีเวลาใช้งานจริง (TTL) ต่ำมาก จากนั้นตรวจสอบอินสแตนซ์ Memcached ระยะไกล (ด้วย TTL ที่สูงกว่า) ก่อนที่จะถามฐานข้อมูลโดยตรง ซึ่งช่วยให้เราลดการอ่านฐานข้อมูลสำหรับเอกสารทั่วไปได้อย่างมาก เช่น การตั้งค่าของลูกค้าหรือรายละเอียดแคมเปญ “ในที่สุด” อาจฟังดูน่ากลัว แต่ในความเป็นจริง มันใช้เวลาเพียงไม่กี่วินาที และการใช้วิธีนี้จะลดปริมาณการรับส่งข้อมูลจำนวนมหาศาลจากแหล่งที่มาของความจริง หากคุณเคยเรียนวิชาสถาปัตยกรรมคอมพิวเตอร์มาก่อน คุณอาจรู้ว่าวิธีนี้คล้ายกับวิธีการทำงานของระบบแคช L1, L2 และ L3 ของ CPU!

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

ที่ที่ทับทิมและรางพอดี

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

Ruby and Rails มีประวัติที่พิสูจน์แล้วในการช่วยให้ทีมทำซ้ำได้อย่างรวดเร็วเมื่อ Braze เริ่มต้นในปี 2011 และยังคงใช้งานโดย GitHub, Shopify และแบรนด์ชั้นนำอื่นๆ เนื่องจากยังคงทำให้สิ่งนี้เป็นไปได้ พวกเขายังคงได้รับการพัฒนาอย่างแข็งขันโดยชุมชน Ruby และ Rails ตามลำดับ และทั้งคู่ยังคงมีไลบรารีโอเพ่นซอร์สที่ยอดเยี่ยมสำหรับความต้องการที่หลากหลาย คู่นี้เป็นตัวเลือกที่ยอดเยี่ยมสำหรับการทำซ้ำอย่างรวดเร็ว เนื่องจากมีความยืดหยุ่นจำนวนมหาศาล และรักษาความเรียบง่ายไว้เป็นจำนวนมากสำหรับกรณีการใช้งานทั่วไป เราพบว่าเป็นจริงอย่างท่วมท้นทุกวันที่เราใช้

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

เมื่อเราไม่ใช้ทับทิม

แต่เดี๋ยวก่อน! ไม่ใช่ทุกสิ่งที่เราทำที่ Braze อยู่ที่ Ruby มีสถานที่บางแห่งในช่วงหลายปีที่ผ่านมาที่เราได้เรียกร้องให้นำสิ่งต่าง ๆ ไปสู่ภาษาและเทคโนโลยีอื่น ๆ ด้วยเหตุผลหลายประการ ลองมาดูสามคนนี้เพื่อให้ข้อมูลเชิงลึกเพิ่มเติมว่าเมื่อใดที่เราทำและไม่พึ่งพา Ruby

1. บริการผู้ส่ง

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

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

2. ขั้วต่อกระแส

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

3. การเรียนรู้ของเครื่อง

หากคุณเคยทำงานด้านการเรียนรู้ของเครื่องมาก่อน คุณจะรู้ว่าภาษาที่เลือกคือ Python แพ็คเกจและเครื่องมือมากมายสำหรับเวิร์กโหลดแมชชีนเลิร์นนิงใน Python eclipse ที่รองรับ Ruby ที่เทียบเท่ากัน—เช่น โน้ตบุ๊ก TensorFlow และ Jupyter เป็นเครื่องมือสำหรับทีมของเรา และเครื่องมือประเภทนั้นไม่มีอยู่จริงหรือไม่มีอยู่จริงในโลกของ Ruby ด้วยเหตุนี้ เราจึงหันมาใช้ Python ในการสร้างองค์ประกอบของผลิตภัณฑ์ของเราที่ใช้ประโยชน์จากการเรียนรู้ของเครื่อง

เมื่อภาษามีความสำคัญ

เห็นได้ชัดว่าเรามีตัวอย่างที่ดีสองสามตัวอย่างด้านบนที่ Ruby ไม่ใช่ตัวเลือกในอุดมคติ มีเหตุผลมากมายที่คุณอาจเลือกภาษาอื่น นี่คือเหตุผลบางส่วนที่เราคิดว่ามีประโยชน์อย่างยิ่งที่จะต้องพิจารณา

สร้างสิ่งใหม่โดยไม่ต้องเปลี่ยนต้นทุน

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

ระบบนิเวศภาษาเฉพาะงานและการยศาสตร์

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

ความเร็วในการดำเนินการ

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

ความคุ้นเคยของนักพัฒนา

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

ความคิดสุดท้าย

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

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

สนใจทำงานที่ Braze หรือไม่? เรากำลังจ้างบทบาทที่หลากหลายในทีมวิศวกรรม การจัดการผลิตภัณฑ์ และประสบการณ์ผู้ใช้ ตรวจสอบหน้าอาชีพของเรา เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับบทบาทที่เปิดกว้างและวัฒนธรรมของเรา