การปรับรื้อระบบซอฟต์แวร์: จากสปาเก็ตตี้ไปจนถึงการออกแบบที่สะอาด
เผยแพร่แล้ว: 2022-03-11คุณสามารถดูระบบของเราได้หรือไม่? ผู้เขียนซอฟต์แวร์ไม่อยู่แล้ว และเราประสบปัญหาหลายประการ เราต้องการใครสักคนที่จะดูมันและทำความสะอาดให้เรา
ใครก็ตามที่เคยทำงานด้านวิศวกรรมซอฟต์แวร์มาระยะหนึ่งแล้วจะรู้ดีว่าคำขอที่ดูไร้เดียงสานี้มักจะเป็นจุดเริ่มต้นของโครงการที่ การสืบทอดโค้ดของผู้อื่นอาจเป็นฝันร้าย โดยเฉพาะอย่างยิ่งเมื่อโค้ดได้รับการออกแบบมาไม่ดีและไม่มีเอกสารประกอบ
เมื่อเร็วๆ นี้ฉันได้รับคำขอจากลูกค้ารายหนึ่งให้ตรวจสอบแอปพลิเคชันเซิร์ฟเวอร์แชท socket.io ที่มีอยู่ (เขียนด้วย Node.js) และปรับปรุงให้ดีขึ้น ฉันจึงระมัดระวังเป็นอย่างยิ่ง แต่ก่อนจะวิ่งขึ้นเขา อย่างน้อยก็ตกลงว่าจะไปดูโค้ดกันก่อน
น่าเสียดายที่การดูรหัสยืนยันข้อกังวลของฉันอีกครั้งเท่านั้น เซิร์ฟเวอร์แชทนี้ได้รับการติดตั้งเป็นไฟล์ JavaScript ขนาดใหญ่เพียงไฟล์เดียว การรื้อปรับโครงสร้างไฟล์เสาหินเดียวนี้ให้เป็นซอฟต์แวร์ที่ออกแบบอย่างหมดจดและดูแลรักษาง่ายนั้นเป็นสิ่งที่ท้าทายอย่างแท้จริง แต่ฉันชอบความท้าทาย ฉันก็เลยตกลงไป
จุดเริ่มต้น - เตรียมพร้อมสำหรับการปรับรื้อปรับระบบ
ซอฟต์แวร์ที่มีอยู่ประกอบด้วยไฟล์เดียวที่มีรหัส 1,200 บรรทัดที่ไม่มีเอกสาร เย้ๆ นอกจากนี้ เป็นที่ทราบกันดีว่ามีข้อบกพร่องบางประการและมีปัญหาด้านประสิทธิภาพบางอย่าง
นอกจากนี้ การตรวจสอบไฟล์บันทึก (เป็นจุดเริ่มต้นที่ดีเสมอเมื่อรับรหัสของผู้อื่น) เผยให้เห็นถึงปัญหาหน่วยความจำรั่วที่อาจเกิดขึ้น ในบางจุด มีรายงานว่ากระบวนการนี้ใช้ RAM มากกว่า 1GB
จากปัญหาเหล่านี้ จึงเป็นที่แน่ชัดในทันทีว่าโค้ดจะต้องได้รับการจัดระเบียบใหม่และปรับให้เป็นโมดูล ก่อนที่จะพยายามแก้ไขจุดบกพร่องหรือปรับปรุงตรรกะทางธุรกิจ ในตอนท้ายปัญหาเบื้องต้นบางอย่างที่จำเป็นต้องแก้ไข ได้แก่ :
- โครงสร้างรหัส โค้ดไม่มีโครงสร้างที่แท้จริง ทำให้ยากต่อการแยกแยะการกำหนดค่าจากโครงสร้างพื้นฐานจากตรรกะทางธุรกิจ โดยพื้นฐานแล้วไม่มีการแยกส่วนหรือการแยกข้อกังวล
- รหัสซ้ำซ้อน โค้ดบางส่วน (เช่น รหัสจัดการข้อผิดพลาดสำหรับตัวจัดการเหตุการณ์ทุกตัว รหัสสำหรับส่งคำขอเว็บ ฯลฯ) ถูกทำซ้ำหลายครั้ง โค้ดที่ซ้ำกันไม่เคยเป็นสิ่งที่ดี ทำให้โค้ดดูแลรักษายากขึ้นอย่างมาก และมีแนวโน้มที่จะเกิดข้อผิดพลาดมากขึ้น (เมื่อโค้ดที่ซ้ำซ้อนได้รับการแก้ไขหรืออัปเดตในที่หนึ่งแต่ไม่ได้รับการแก้ไขในที่อื่น)
- ค่าฮาร์ดโค้ด รหัสมีค่าฮาร์ดโค้ดจำนวนหนึ่ง (ไม่ค่อยดี) ความสามารถในการแก้ไขค่าเหล่านี้ผ่านพารามิเตอร์การกำหนดค่า (แทนที่จะต้องเปลี่ยนค่าฮาร์ดโค้ดในโค้ด) จะเพิ่มความยืดหยุ่นและยังช่วยอำนวยความสะดวกในการทดสอบและการดีบัก
- การบันทึก ระบบการบันทึกเป็นพื้นฐานมาก มันจะสร้างไฟล์บันทึกขนาดยักษ์ไฟล์เดียวที่ยากและงุ่มง่ามในการวิเคราะห์หรือแยกวิเคราะห์
วัตถุประสงค์ทางสถาปัตยกรรมที่สำคัญ
ในกระบวนการเริ่มต้นปรับโครงสร้างโค้ดใหม่ นอกเหนือจากการแก้ไขปัญหาเฉพาะที่ระบุข้างต้นแล้ว ฉันต้องการเริ่มระบุวัตถุประสงค์ทางสถาปัตยกรรมที่สำคัญบางประการที่ (หรืออย่างน้อยควรเป็น) ร่วมกันกับการออกแบบระบบซอฟต์แวร์ใดๆ . ซึ่งรวมถึง:
- การบำรุงรักษา อย่าเขียนซอฟต์แวร์โดยหวังว่าจะเป็นคนเดียวที่จะต้องบำรุงรักษาซอฟต์แวร์นี้ พิจารณาเสมอว่าโค้ดของคุณจะเข้าใจผู้อื่นได้ดีเพียงใด และพวกเขาจะแก้ไขหรือแก้ไขข้อบกพร่องได้ง่ายเพียงใด
- ความสามารถในการขยาย อย่าทึกทักเอาเองว่าฟังก์ชันที่คุณใช้งานอยู่ในปัจจุบันเป็นสิ่งที่จำเป็น ออกแบบซอฟต์แวร์ของคุณในลักษณะที่จะขยายได้ง่าย
- โมดูลาร์ แยกการทำงานออกเป็นโมดูลเชิงตรรกะและชัดเจน แต่ละโมดูลมีจุดประสงค์และหน้าที่ที่ชัดเจน
- ความสามารถในการปรับขนาด ผู้ใช้ในปัจจุบันเริ่มหมดความอดทนมากขึ้น โดยคาดหวังว่าจะมีเวลาตอบสนองในทันที (หรืออย่างน้อยก็ใกล้ถึงในทันที) ประสิทธิภาพต่ำและเวลาแฝงสูงอาจทำให้แม้แต่แอปพลิเคชันที่มีประโยชน์ที่สุดก็ล้มเหลวในตลาดได้ ซอฟต์แวร์ของคุณจะทำงานอย่างไรเมื่อจำนวนผู้ใช้พร้อมกันและความต้องการแบนด์วิดท์เพิ่มขึ้น เทคนิคต่างๆ เช่น การทำให้ขนานกัน การปรับฐานข้อมูลให้เหมาะสม และการประมวลผลแบบอะซิงโครนัสสามารถช่วยปรับปรุงความสามารถของระบบในการตอบสนองได้ แม้ว่าความต้องการโหลดและทรัพยากรจะเพิ่มขึ้น
การปรับโครงสร้างรหัส
เป้าหมายของเราคือเปลี่ยนจากไฟล์ซอร์สโค้ด mongo แบบเสาหินเดียวไปเป็นชุดส่วนประกอบแบบแยกส่วนซึ่งได้รับการออกแบบทางสถาปัตยกรรมอย่างหมดจด โค้ดที่ได้ควรดูแลรักษา ปรับปรุง และแก้ปัญหาได้ง่ายขึ้นอย่างมาก
สำหรับแอปพลิเคชันนี้ ฉันได้ตัดสินใจจัดระเบียบโค้ดลงในส่วนประกอบทางสถาปัตยกรรมที่แตกต่างกันดังต่อไปนี้:
- app.js - นี่คือจุดเริ่มต้น รหัสของเราจะเรียกใช้จากที่นี่
- config - นี่คือที่ที่การตั้งค่าการกำหนดค่าของเราจะอยู่
- ioW - “ตัวห่อหุ้ม IO” ที่จะมีตรรกะ IO (และธุรกิจ) ทั้งหมด
- การบันทึก - รหัสที่เกี่ยวข้องกับการบันทึกทั้งหมด (โปรดทราบว่าโครงสร้างไดเร็กทอรีจะรวมโฟลเดอร์
logs
ใหม่ซึ่งจะมีไฟล์บันทึกทั้งหมดด้วย) - package.json - รายการการขึ้นต่อกันของแพ็คเกจสำหรับ Node.js
- node_modules - โมดูลทั้งหมดที่ Node.js . ต้องการ
แนวทางเฉพาะนี้ไม่มีความมหัศจรรย์ อาจมีหลายวิธีในการปรับโครงสร้างโค้ดใหม่ โดยส่วนตัวแล้วฉันรู้สึกว่าองค์กรนี้สะอาดเพียงพอและมีการจัดระเบียบอย่างดีโดยไม่ซับซ้อนเกินไป
ไดเร็กทอรีผลลัพธ์และการจัดระเบียบไฟล์แสดงอยู่ด้านล่าง
การบันทึก
แพ็คเกจการบันทึกได้รับการพัฒนาสำหรับสภาพแวดล้อมการพัฒนาและภาษาส่วนใหญ่ในปัจจุบัน ดังนั้นจึงเป็นเรื่องยากในปัจจุบันที่คุณจะต้อง "ม้วน" ความสามารถในการบันทึกของคุณเอง
เนื่องจากเรากำลังทำงานกับ Node.js ฉันจึงเลือก log4js-node ซึ่งโดยพื้นฐานแล้วจะเป็นเวอร์ชันของไลบรารี log4js สำหรับใช้กับ Node.js ไลบรารี่นี้มีคุณสมบัติเจ๋งๆ บางอย่าง เช่น ความสามารถในการบันทึกข้อความได้หลายระดับ (คำเตือน ข้อผิดพลาด ฯลฯ) และเราสามารถมีไฟล์โรลลิ่งที่สามารถแบ่งได้ เช่น แบบรายวัน ดังนั้นเราจึงไม่ต้อง จัดการกับไฟล์ขนาดใหญ่ที่จะใช้เวลานานในการเปิดและยากต่อการวิเคราะห์และแยกวิเคราะห์
เพื่อจุดประสงค์ของเรา ฉันได้สร้าง wrapper ขนาดเล็กรอบๆ log4js-node เพื่อเพิ่มความสามารถเฉพาะที่ต้องการเพิ่มเติม โปรดทราบว่าฉันได้เลือกที่จะสร้าง wrapper รอบ ๆ log4js-node ซึ่งฉันจะใช้ตลอดทั้งโค้ดของฉัน สิ่งนี้จะปรับการใช้งานความสามารถในการบันทึกแบบขยายเหล่านี้ในที่เดียว จึงหลีกเลี่ยงความซ้ำซ้อนและความซับซ้อนที่ไม่จำเป็นตลอดทั้งโค้ดของฉันเมื่อฉันเรียกใช้การบันทึก
เนื่องจากเรากำลังทำงานกับ I/O และเราจะมีไคลเอนต์ (ผู้ใช้) หลายรายที่จะทำให้เกิดการเชื่อมต่อ (ซ็อกเก็ต) หลายตัว ฉันต้องการติดตามกิจกรรมของผู้ใช้เฉพาะในไฟล์บันทึก และต้องการทราบด้วย แหล่งที่มาของรายการบันทึกแต่ละรายการ ดังนั้นฉันจึงคาดหวังว่าจะมีรายการบันทึกเกี่ยวกับสถานะของแอปพลิเคชัน และรายการบันทึกบางอย่างที่เกี่ยวข้องกับกิจกรรมของผู้ใช้โดยเฉพาะ
ในโค้ดของ wrapper การบันทึกของฉัน ฉันสามารถจับคู่ ID ผู้ใช้และซ็อกเก็ต ซึ่งจะทำให้ฉันสามารถติดตามการดำเนินการที่ดำเนินการก่อนและหลังเหตุการณ์ ERROR ได้ Wrapper การบันทึกจะอนุญาตให้ฉันสร้างตัวบันทึกที่แตกต่างกันด้วยข้อมูลตามบริบทที่แตกต่างกัน ซึ่งฉันสามารถส่งต่อไปยังตัวจัดการเหตุการณ์ เพื่อให้ฉันทราบแหล่งที่มาของรายการบันทึก
รหัสสำหรับ Wrapper การบันทึกมีอยู่ที่นี่
การกำหนดค่า
บ่อยครั้งจำเป็นต้องสนับสนุนการกำหนดค่าต่างๆ สำหรับระบบ ความแตกต่างเหล่านี้อาจเป็นความแตกต่างระหว่างสภาพแวดล้อมการพัฒนาและการผลิต หรือแม้กระทั่งขึ้นอยู่กับความจำเป็นในการแสดงสภาพแวดล้อมของลูกค้าและสถานการณ์การใช้งานที่แตกต่างกัน
แทนที่จะต้องเปลี่ยนแปลงโค้ดเพื่อรองรับสิ่งนี้ แนวปฏิบัติทั่วไปคือการควบคุมความแตกต่างในลักษณะการทำงานโดยใช้พารามิเตอร์การกำหนดค่า ในกรณีของฉัน ฉันต้องการความสามารถในการมีสภาพแวดล้อมการดำเนินการที่แตกต่างกัน (การจัดเตรียมและการใช้งานจริง) ซึ่งอาจมีการตั้งค่าต่างกัน ฉันยังต้องการให้แน่ใจว่าโค้ดที่ทดสอบทำงานได้ดีทั้งในการแสดงละครและการใช้งานจริง และหากฉันจำเป็นต้องเปลี่ยนรหัสเพื่อจุดประสงค์นี้ ก็จะทำให้กระบวนการทดสอบเป็นโมฆะ
เมื่อใช้ตัวแปรสภาพแวดล้อม Node.js ฉันสามารถระบุไฟล์การกำหนดค่าที่ฉันต้องการใช้สำหรับการดำเนินการเฉพาะได้ ดังนั้นฉันจึงย้ายพารามิเตอร์การกำหนดค่าที่ฮาร์ดโค้ดก่อนหน้านี้ทั้งหมดไปยังไฟล์การกำหนดค่า และสร้างโมดูลการกำหนดค่าอย่างง่ายที่โหลดไฟล์กำหนดค่าที่เหมาะสมด้วยการตั้งค่าที่ต้องการ ฉันยังจัดหมวดหมู่การตั้งค่าทั้งหมดเพื่อบังคับใช้ระดับองค์กรในไฟล์การกำหนดค่าและเพื่อให้นำทางง่ายขึ้น

ต่อไปนี้คือตัวอย่างไฟล์กำหนดค่าที่เป็นผลลัพธ์:
{ "app": { "port": 8889, "invRepeatInterval":1000, "invTimeOut":300000, "chatLogInterval":60000, "updateUsersInterval":600000, "dbgCurrentStatusInterval":3600000, "roomDelimiter":"_", "roomPrefix":"/" }, "webSite":{ "host": "mysite.com", "port": 80, "friendListHandler":"/MyMethods.aspx/FriendsList", "userCanChatHandler":"/MyMethods.aspx/UserCanChat", "chatLogsHandler":"/MyMethods.aspx/SaveLogs" }, "logging": { "appenders": [ { "type": "dateFile", "filename": "logs/chat-server", "pattern": "-yyyy-MM-dd", "alwaysIncludePattern": false } ], "level": "DEBUG" } }
รหัส Flow
จนถึงตอนนี้ เราได้สร้างโครงสร้างโฟลเดอร์เพื่อโฮสต์โมดูลต่างๆ เราได้ตั้งค่าวิธีการโหลดข้อมูลเฉพาะของสภาพแวดล้อม และสร้างระบบการบันทึก ดังนั้นเรามาดูกันว่าเราจะเชื่อมโยงชิ้นส่วนทั้งหมดเข้าด้วยกันได้อย่างไรโดยไม่ต้องเปลี่ยนรหัสเฉพาะธุรกิจ
ด้วยโครงสร้างโมดูลาร์ใหม่ของโค้ด ทำให้ app.js
จุดเริ่มต้นของเรานั้นเรียบง่ายเพียงพอ โดยมีโค้ดเริ่มต้นเท่านั้น:
var config = require('./config'); var logging = require('./logging'); var ioW = require('./ioW'); var obj = config.getCurrent(); logging.initialize(obj.logging); ioW.initialize(config);
เมื่อเรากำหนดโครงสร้างโค้ดของเรา เราบอกว่าโฟลเดอร์ ioW
จะเก็บโค้ดที่เกี่ยวข้องกับธุรกิจและ socket.io โดยเฉพาะอย่างยิ่ง มันจะประกอบด้วยไฟล์ต่อไปนี้ (โปรดทราบว่าคุณสามารถคลิกที่ชื่อไฟล์ใดก็ได้ในรายการเพื่อดูซอร์สโค้ดที่เกี่ยวข้อง):
-
index.js
- จัดการการเริ่มต้นและการเชื่อมต่อ socket.io รวมถึงการสมัครสมาชิกเหตุการณ์รวมถึงตัวจัดการข้อผิดพลาดส่วนกลางสำหรับเหตุการณ์ -
eventManager.js
– โฮสต์ตรรกะที่เกี่ยวข้องกับธุรกิจทั้งหมด (ตัวจัดการเหตุการณ์) -
webHelper.js
– วิธีการช่วยเหลือสำหรับการร้องขอเว็บ -
linkedList.js
– คลาสยูทิลิตี้รายการลิงก์
เราปรับโครงสร้างโค้ดที่ส่งคำขอเว็บและย้ายไปยังไฟล์แยกต่างหาก และเราจัดการเพื่อให้ตรรกะทางธุรกิจของเราอยู่ในที่เดียวกันและไม่มีการแก้ไข
หมายเหตุสำคัญประการหนึ่ง: ในขั้นตอนนี้ eventManager.js
ยังคงมีฟังก์ชันตัวช่วยบางอย่างที่ควรแยกออกเป็นโมดูลแยกต่างหาก อย่างไรก็ตาม เนื่องจากเป้าหมายของเราในรอบแรกนี้คือจัดระเบียบโค้ดใหม่ในขณะที่ลดผลกระทบต่อตรรกะทางธุรกิจให้เหลือน้อยที่สุด และฟังก์ชันตัวช่วยเหล่านี้เชื่อมโยงกับตรรกะทางธุรกิจที่ซับซ้อนเกินไป เราจึงเลือกที่จะเลื่อนการดำเนินการนี้ไปยังขั้นตอนถัดไปเพื่อปรับปรุงองค์กรของ รหัส.
เนื่องจาก Node.js ไม่ตรงกันตามคำจำกัดความ เรามักพบ "callback hell" รังของหนู ซึ่งทำให้โค้ดนำทางและแก้ปัญหาได้ยากเป็นพิเศษ เพื่อหลีกเลี่ยงหลุมพรางนี้ ในการปรับใช้ใหม่ของฉัน ฉันได้ใช้รูปแบบคำสัญญาและใช้ประโยชน์จากบลูเบิร์ดโดยเฉพาะ ซึ่งเป็นห้องสมุดสัญญาที่ดีและรวดเร็วมาก คำมั่นสัญญาจะช่วยให้เราสามารถติดตามโค้ดได้เหมือนกับว่าโค้ดเป็นแบบซิงโครนัสและยังให้การจัดการข้อผิดพลาดและวิธีที่สะอาดในการสร้างมาตรฐานการตอบกลับระหว่างการโทร มีสัญญาโดยนัยในรหัสของเราที่ตัวจัดการเหตุการณ์ทุกคนต้องส่งคืนสัญญา เพื่อให้เราสามารถจัดการการจัดการข้อผิดพลาดจากส่วนกลางและการบันทึก
ตัวจัดการเหตุการณ์ทั้งหมดจะส่งคืนสัญญา (ไม่ว่าพวกเขาจะโทรแบบอะซิงโครนัสหรือไม่ก็ตาม) ด้วยสิ่งนี้ เราสามารถรวมศูนย์การจัดการข้อผิดพลาดและการบันทึก และเรามั่นใจว่าหากเรามีข้อผิดพลาดที่ไม่สามารถจัดการได้ในตัวจัดการเหตุการณ์ ข้อผิดพลาดนั้นจะถูกตรวจจับได้
function execEventHandler(socket, eventName, eventHandler, data){ var sLogger = logging.createLogger(socket.id + ' - ' + eventName); sLogger.info(''); eventHandler(socket, data, sLogger).then(null, function(err){ sLogger.error(err.stack); }); };
ในการสนทนาเกี่ยวกับการบันทึก เรากล่าวว่าทุกการเชื่อมต่อจะมีตัวบันทึกของตัวเองพร้อมข้อมูลตามบริบท โดยเฉพาะอย่างยิ่ง เรากำลังผูก ID ซ็อกเก็ตและชื่อเหตุการณ์กับตัวบันทึกเมื่อเราสร้างมันขึ้นมา ดังนั้นเมื่อเราส่งตัวบันทึกนั้นไปยังตัวจัดการเหตุการณ์ ทุกบรรทัดบันทึกจะมีข้อมูลนั้นอยู่ในนั้น:
var sLogger = logging.createLogger(socket.id + ' - ' + eventName);
อีกประเด็นหนึ่งที่น่ากล่าวถึงเกี่ยวกับการจัดการเหตุการณ์: ในไฟล์ต้นฉบับ เรามีการเรียกใช้ฟังก์ชัน setInterval
ที่อยู่ภายในตัวจัดการเหตุการณ์ของเหตุการณ์การเชื่อมต่อ socket.io และเราได้ระบุฟังก์ชันนี้ว่าเป็นปัญหา
io.on('connection', function (socket) { ... Several event handlers .... setInterval(function() { try { var date = Date.now(); var tmp = []; while (0 < messageHub.count() && messageHub.head().date < date) { var item = messageHub.remove(); tmp.push(item); } ... Post Data to an external web service... } catch (e) { log('ERROR: ex: ' + e); } }, CHAT_LOGS_INTERVAL); });
รหัสนี้สร้างตัวจับเวลาด้วยช่วงเวลาที่กำหนด (ในกรณีของเราคือ 1 นาที) สำหรับ ทุกคำขอเชื่อมต่อ ที่เราได้รับ ตัวอย่างเช่น หาก ณ เวลาใดก็ตาม เรามีซ็อกเก็ตออนไลน์ 300 ซ็อกเก็ต เราก็จะมีตัวจับเวลา 300 ตัวดำเนินการทุกนาที ปัญหาของสิ่งนี้ ดังที่คุณเห็นในโค้ดด้านบน คือไม่มีการใช้งานซ็อกเก็ตหรือตัวแปรใดๆ ที่กำหนดไว้ภายในขอบเขตของตัวจัดการเหตุการณ์ ตัวแปรเดียวที่ใช้คือตัวแปร messageHub
ที่ประกาศในระดับโมดูล ซึ่งหมายความว่าจะเหมือนกันสำหรับการเชื่อมต่อทั้งหมด ดังนั้นจึงไม่จำเป็นต้องมีตัวจับเวลาแยกต่างหากต่อการเชื่อมต่อ ดังนั้นเราจึงลบสิ่งนี้ออกจากตัวจัดการเหตุการณ์การเชื่อมต่อและรวมไว้ในรหัสการเริ่มต้นทั่วไปของเรา ซึ่งในกรณีนี้คือฟังก์ชัน initialize
สุดท้าย ในการประมวลผลการตอบกลับ ใน webHelper.js
เราได้เพิ่มการประมวลผลสำหรับการตอบกลับที่ไม่รู้จักซึ่งจะบันทึกข้อมูลซึ่งจะเป็นประโยชน์ต่อกระบวนการดีบัก:
if (!res || !res.d || !res.d.IsValid){ logger.debug(sendData); logger.debug(data); reject(new Error('Request failed. Path ' + params.path + ' . Invalid return data.')); return; }
ขั้นตอนสุดท้ายคือการตั้งค่าไฟล์บันทึกสำหรับข้อผิดพลาดมาตรฐานของ Node.js ไฟล์นี้จะมีข้อผิดพลาดที่ไม่สามารถจัดการได้ซึ่งเราอาจพลาดไป สำหรับการตั้งค่ากระบวนการโหนดใน Windows (ไม่ใช่ในอุดมคติ แต่คุณรู้…) เป็นบริการ เราใช้เครื่องมือที่เรียกว่า nssm ซึ่งมี UI ภาพที่ช่วยให้คุณกำหนดไฟล์เอาต์พุตมาตรฐาน ไฟล์ข้อผิดพลาดมาตรฐาน และตัวแปรสภาพแวดล้อม
เกี่ยวกับประสิทธิภาพของ Node.js
Node.js เป็นภาษาการเขียนโปรแกรมแบบเธรดเดียว เพื่อปรับปรุงความสามารถในการปรับขนาด มีหลายทางเลือกที่เราสามารถใช้ได้ มีโมดูลคลัสเตอร์โหนดหรือเพียงแค่เพิ่มกระบวนการโหนดเพิ่มเติม และวาง nginx ไว้ด้านบนสุดเพื่อทำการส่งต่อและปรับสมดุลโหลด
ในกรณีของเรา เนื่องจากทุกกระบวนการย่อยของคลัสเตอร์โหนดหรือกระบวนการโหนดจะมีพื้นที่หน่วยความจำของตัวเอง เราจึงไม่สามารถแบ่งปันข้อมูลระหว่างกระบวนการเหล่านั้นได้อย่างง่ายดาย ดังนั้นสำหรับกรณีนี้ เราจะต้องใช้ที่เก็บข้อมูลภายนอก (เช่น redis) เพื่อให้ซ็อกเก็ตออนไลน์พร้อมใช้งานสำหรับกระบวนการต่างๆ
บทสรุป
ทั้งหมดนี้ทำให้เราสามารถล้างโค้ดที่ส่งมาให้เราในตอนแรกได้สำเร็จ นี่ไม่ได้เกี่ยวกับการทำให้โค้ดสมบูรณ์แบบ แต่เกี่ยวกับการปรับรื้อระบบใหม่เพื่อสร้างรากฐานทางสถาปัตยกรรมที่สะอาดซึ่งจะง่ายต่อการสนับสนุนและบำรุงรักษา ซึ่งจะอำนวยความสะดวกและทำให้การดีบักง่ายขึ้น
การปฏิบัติตามหลักการออกแบบซอฟต์แวร์ที่สำคัญที่แจกแจงไว้ก่อนหน้านี้ ได้แก่ ความสามารถในการบำรุงรักษา การขยาย ความเป็นโมดูล และความสามารถในการปรับขนาด เราได้สร้างโมดูลและโครงสร้างโค้ดที่ระบุความรับผิดชอบของโมดูลต่างๆ อย่างชัดเจนและชัดเจน เรายังพบปัญหาบางประการในการใช้งานแบบเดิมซึ่งนำไปสู่การใช้หน่วยความจำสูงซึ่งทำให้ประสิทธิภาพการทำงานลดลง
หวังว่าคุณจะชอบบทความนี้ โปรดแจ้งให้เราทราบหากคุณมีความคิดเห็นหรือคำถามเพิ่มเติม