โครงสร้างสแต็กซอฟต์แวร์แบบดั้งเดิมของแอปพลิเคชันออนไลน์ Taobao คือ Nginx + Velocity + Java, I.e.:
ในระบบนี้ Nginx ส่งต่อการร้องขอไปยังแอปพลิเคชัน Java ซึ่งจัดการการทำธุรกรรมและแสดงผลข้อมูลลงในหน้าสุดท้ายโดยใช้เทมเพลตความเร็ว
หลังจากแนะนำ node.js เราจะประสบปัญหาต่อไปนี้อย่างหลีกเลี่ยงไม่ได้:
วิธีการออกแบบโครงสร้างทอพอโลยีของสแต็คเทคโนโลยีและวิธีการเลือกวิธีการปรับใช้มันถือว่าเป็นวิทยาศาสตร์และสมเหตุสมผลหรือไม่? หลังจากโครงการเสร็จสมบูรณ์แล้ววิธีการแบ่งปริมาณการใช้งานซึ่งสะดวกและรวดเร็วสำหรับการดำเนินงานและการบำรุงรักษา? เมื่อพบปัญหาออนไลน์วิธีกำจัดอันตรายโดยเร็วที่สุดและหลีกเลี่ยงการสูญเสียมากขึ้น? จะมั่นใจได้อย่างไรว่าสุขภาพของแอปพลิเคชันและจัดการในระดับการจัดตารางการปรับสมดุลโหลด? โทโพโลยีของระบบ
ตามความคิดและการฝึกฝนของเราเกี่ยวกับการแยกด้านหน้าและด้านหลัง (II) - ขึ้นอยู่กับการสำรวจเทมเพลตของปลายด้านหน้าและด้านหลังความเร็วจะต้องถูกแทนที่ด้วย node.js เพื่อให้โครงสร้างนี้กลายเป็น:
นี่คือเป้าหมายในอุดมคติ อย่างไรก็ตามการแนะนำครั้งแรกของเลเยอร์ Node.js ในสแต็กแบบดั้งเดิมเป็นความพยายามใหม่หลังจากทั้งหมด เพื่อความปลอดภัยเราตัดสินใจที่จะเปิดใช้งานเทคโนโลยีใหม่ ๆ ในหน้ารายการโปรด (shoucang.taobao.com/item_collect.htm) ของรายการโปรดของเราในขณะที่หน้าอื่น ๆ ยังคงใช้โซลูชั่นดั้งเดิม นั่นคือ Nginx กำหนดประเภทหน้าของคำขอและกำหนดว่าจะส่งคำขอไปยัง node.js หรือ Java หรือไม่ ดังนั้นโครงสร้างสุดท้ายจึงกลายเป็น:
แผนการปรับใช้
โครงสร้างข้างต้นดูเหมือนจะดี แต่ในความเป็นจริงปัญหาใหม่ยังคงรออยู่ด้านหน้า ในโครงสร้างดั้งเดิม Nginx และ Java จะถูกปรับใช้บนเซิร์ฟเวอร์เดียวกัน Nginx ฟังพอร์ต 80 และสื่อสารกับ Java ฟังพอร์ต 7001 ที่บิตสูง ตอนนี้มีการแนะนำ Node.js แล้วกระบวนการใหม่ที่จำเป็นต้องใช้พอร์ตการฟังเป็นสิ่งจำเป็น ควรปรับใช้ node.js บนเครื่องเดียวกันด้วย nginx + java หรือ node.js จะถูกปรับใช้ในคลัสเตอร์แยกต่างหากหรือไม่?
มาเปรียบเทียบคุณสมบัติของทั้งสองวิธี:
รายการโปรดของ Taobao เป็นแอปพลิเคชั่นที่มี PV เฉลี่ยต่อวันหลายสิบล้านซึ่งมีข้อกำหนดที่สูงมากสำหรับความมั่นคง (อันที่จริงความไม่แน่นอนออนไลน์ของผลิตภัณฑ์ใด ๆ ไม่สามารถยอมรับได้) หากคุณใช้โซลูชันการปรับใช้คลัสเตอร์เดียวกันคุณจะต้องแจกจ่ายไฟล์หนึ่งครั้งและรีบูตสองครั้งเพื่อให้รีลีสเสร็จสมบูรณ์ ในกรณีที่คุณต้องการย้อนกลับคุณจะต้องใช้งานแพ็คเกจพื้นฐานเพียงครั้งเดียว ในแง่ของประสิทธิภาพการปรับใช้คลัสเตอร์เดียวกันก็มีข้อได้เปรียบทางทฤษฎีบางอย่าง (แม้ว่าแบนด์วิดท์สวิตช์และเวลาแฝงของอินทราเน็ตนั้นมองโลกในแง่ดีมาก) สำหรับความสัมพันธ์แบบหนึ่งถึงหลายต่อหลายครั้งหรือหลายต่อหนึ่งมันอาจใช้งานได้อย่างเต็มที่ในทางทฤษฎีโดยเซิร์ฟเวอร์ แต่เมื่อเทียบกับข้อกำหนดด้านเสถียรภาพจุดนี้ไม่เร่งด่วนที่จะแก้ไข ดังนั้นในการเปลี่ยนแปลงของรายการโปรดเราเลือกโซลูชันการปรับใช้คลัสเตอร์เดียวกัน
สีเทา
เพื่อให้แน่ใจว่ามีเสถียรภาพสูงสุดการแปลงนี้ไม่ได้ลบรหัสความเร็วโดยตรงอย่างสมบูรณ์ มีเซิร์ฟเวอร์เกือบ 100 ตัวในคลัสเตอร์แอปพลิเคชัน เราใช้เซิร์ฟเวอร์เป็น granularity และค่อยๆแนะนำทราฟฟิก กล่าวอีกนัยหนึ่งแม้ว่ากระบวนการ java + node.js กำลังทำงานบนเซิร์ฟเวอร์ทั้งหมดไม่ว่าจะมีกฎการส่งต่อที่สอดคล้องกันใน Nginx กำหนดว่าคำขอที่จะได้รับคอลเลกชันทารกบนเซิร์ฟเวอร์นี้จะถูกประมวลผลผ่าน Node.js. การกำหนดค่าของ nginx คือ:
location = "/item_collect.htm" {proxy_pass http://127.0.0.1:6001; # node.js กระบวนการฟังพอร์ต}เฉพาะเซิร์ฟเวอร์ที่เพิ่มกฎ Nginx นี้จะให้ node.js จัดการกับคำขอที่เกี่ยวข้อง ผ่านการกำหนดค่า NGINX นั้นสะดวกและรวดเร็วในการเพิ่มและลดการรับส่งข้อมูลสีเทาและค่าใช้จ่ายต่ำมาก หากคุณพบปัญหาคุณสามารถย้อนกลับการกำหนดค่า NGINX ได้โดยตรงและกลับไปที่โครงสร้างสแต็กเทคโนโลยีแบบดั้งเดิมทันทีเพื่อบรรเทาอันตราย
เมื่อเราเปิดตัวครั้งแรกเราเปิดใช้งานกฎนี้บนเซิร์ฟเวอร์สองตัวเท่านั้นซึ่งหมายความว่าการรับส่งข้อมูลออนไลน์น้อยกว่า 2% จะถูกประมวลผลใน node.js และคำขอสำหรับการรับส่งข้อมูลที่เหลืออยู่นั้นยังคงแสดงผลด้วยความเร็ว ในอนาคตการรับส่งข้อมูลจะค่อยๆเพิ่มขึ้นขึ้นอยู่กับสถานการณ์และในที่สุดในสัปดาห์ที่สามเซิร์ฟเวอร์ทั้งหมดจะเปิดใช้งาน ณ จุดนี้หน้าคอลเลกชันผลิตภัณฑ์ที่มีปริมาณการใช้งาน 100% ในสภาพแวดล้อมการผลิตจะแสดงผลโดย node.js (คุณสามารถตรวจสอบซอร์สโค้ดเพื่อค้นหาคำหลัก node.js)
เปลี่ยน
กระบวนการสีเทาไม่ราบรื่น ก่อนที่จะตัดการไหลเต็มรูปแบบฉันพบปัญหาบางอย่างไม่ว่าจะเล็กหรือใหญ่ ธุรกิจส่วนใหญ่เกี่ยวข้องกับธุรกิจที่เฉพาะเจาะจงและสิ่งที่ควรค่าแก่การเรียนรู้คือกับดักที่เกี่ยวข้องกับรายละเอียดทางเทคนิค
การตรวจสุขภาพ
ในสถาปัตยกรรมแบบดั้งเดิมระบบการกำหนดเวลาโหลดบาลานซ์จะเริ่มต้นการร้องขอ get ทุกวินาทีถึง URL เฉพาะบนพอร์ต 80 ของแต่ละเซิร์ฟเวอร์และตรวจสอบว่าเซิร์ฟเวอร์นั้นทำงานตามปกติหรือไม่ตามรหัสสถานะ HTTP ที่ส่งคืนคือ 200 หากมีการร้องขอการหมดเวลาหลัง 1S หรือรหัสสถานะ HTTP ไม่ใช่ 200 จะไม่มีการแนะนำการรับส่งข้อมูลไปยังเซิร์ฟเวอร์เพื่อหลีกเลี่ยงปัญหาออนไลน์
เส้นทางของคำขอนี้คือ nginx -> java -> nginx ซึ่งหมายความว่าตราบใดที่ 200 ถูกส่งคืน Nginx และ Java ของเซิร์ฟเวอร์นี้อยู่ในสถานะที่มีสุขภาพดี หลังจากแนะนำ node.js เส้นทางนี้จะกลายเป็น nginx -> node.js -> java -> node.js -> nginx รหัสที่เกี่ยวข้องคือ:
var http = ต้องการ ('http'); app.get ('/status.taobao', ฟังก์ชั่น (req, res) {http.get ({โฮสต์: '127.1', พอร์ต: 7001, เส้นทาง: '/status.taobao'}, ฟังก์ชั่น (res) {res.send (res.statuscode);}) -อย่างไรก็ตามในระหว่างกระบวนการทดสอบพบว่าเมื่อ node.js ส่งต่อคำขอดังกล่าวมันใช้เวลาหลายวินาทีหรือสิบวินาทีเพื่อให้ฝ่ายชวากลับมาทุก ๆ หกหรือเจ็ดครั้ง สิ่งนี้จะทำให้ระบบการกำหนดเวลาโหลดบาลานซ์คิดว่ามีความผิดปกติเกิดขึ้นบนเซิร์ฟเวอร์แล้วตัดทราฟฟิกออก แต่อันที่จริงเซิร์ฟเวอร์สามารถทำงานได้ตามปกติ เห็นได้ชัดว่านี่เป็นปัญหาใหญ่
หลังจากการค้นหาฉันพบว่าโดยค่าเริ่มต้น node.js จะใช้คลาส HTTP Agent เพื่อสร้างการเชื่อมต่อ HTTP คลาสนี้ใช้พูลเชื่อมต่อซ็อกเก็ต ขีด จำกัด สูงสุดเริ่มต้นของจำนวนการเชื่อมต่อสำหรับแต่ละคู่โฮสต์ + พอร์ตคือ 5 ในเวลาเดียวกันคำขอที่เริ่มต้นโดยคลาส HTTP Agent รวมถึง Connection: Keep-Alive ตามค่าเริ่มต้นส่งผลให้การเชื่อมต่อที่ส่งคืนไม่ได้ถูกปล่อยออกมาในเวลาและคำขอที่เริ่มขึ้นในภายหลัง
มีวิธีแก้ปัญหาสุดท้ายสามประการ:
ปิดใช้ HTTP Agent นั่นคือเพิ่มพารามิเตอร์เพิ่มเติม agent: false เมื่อเรียกใช้เมธอด get และรหัสสุดท้ายคือ:
var http = ต้องการ ('http'); app.get ('/status.taobao', ฟังก์ชั่น (req, res) {http.get ({โฮสต์: '127.1', พอร์ต: 7001, ตัวแทน: เท็จ, เส้นทาง: '/status.taobao'}, ฟังก์ชั่น (res) {res.send (res.statuscode); Res.Send (404); ตั้งค่าขีด จำกัด บนของจำนวนซ็อกเก็ตทั่วโลกของวัตถุ http :
http.globalagent.maxsockets = 1,000;
เมื่อคำขอส่งคืนมันจะถูกตัดการเชื่อมต่อในเวลาที่เหมาะสมและเชิงรุก:
http.get (ตัวเลือก, ฟังก์ชั่น (res) {}) on ("ซ็อกเก็ต", ฟังก์ชั่น (ซ็อกเก็ต) {socket.emit ("agentremove"); // ฟังเหตุการณ์ซ็อกเก็ตในทางปฏิบัติเราเลือกวิธีแรก หลังจากการปรับนี้ไม่พบปัญหาอื่น ๆ ในการตรวจสุขภาพ
รวมกัน
การฝึกฝนการรวม Node.js เข้ากับสถานการณ์ทางธุรกิจแบบดั้งเดิมเพิ่งเริ่มขึ้นและยังมีจุดเพิ่มประสิทธิภาพมากมายที่ควรค่าแก่การสำรวจในเชิงลึก ตัวอย่างเช่นหลังจากแอปพลิเคชัน Java มีศูนย์กลางอย่างสมบูรณ์คุณสามารถทดสอบการปรับใช้คลัสเตอร์เพื่อปรับปรุงการใช้งานเซิร์ฟเวอร์ได้หรือไม่? หรือวิธีการเปิดตัวและการย้อนกลับสามารถยืดหยุ่นและควบคุมได้มากขึ้น? รายละเอียดทั้งหมดมีค่าการวิจัยเพิ่มเติม