เมื่อผู้ใช้รายงานปัญหา: เมื่อเกิดข้อผิดพลาดเมื่อใช้ฟังก์ชันออนไลน์บางอย่าง จะสามารถระบุตำแหน่งได้อย่างรวดเร็วและแม่นยำได้อย่างไร? จะติดตามการปรับให้เหมาะสมอย่างมีประสิทธิภาพได้อย่างไรเมื่ออินเทอร์เฟซคำขอบางอย่างส่งคืนข้อมูลช้า
ตามที่เราทุกคนทราบดี เมื่อมีการร้องขอ บันทึกต่อไปนี้อาจจะถูกสร้างขึ้น:
1. AceesLog: บันทึกการเข้าถึงของผู้ใช้
2. ข้อยกเว้น: บันทึกข้อยกเว้นของโค้ด
3. SQL: บันทึกการสืบค้น SQL
4. ThirdParty: Third-
บันทึกการบริการของฝ่าย
จะติดตามบันทึกทั้งหมดที่สร้างโดยการร้องขอได้อย่างไร
วิธีการทั่วไปคือการใช้ requestId เป็นตัวระบุที่ไม่ซ้ำกัน
จากนั้นจึงเขียนมิดเดิลแวร์เพื่อแทรก requestId ลงในบริบท เมื่อจำเป็นต้องทำการบันทึก ให้นำออกจากบริบทสำหรับการพิมพ์
ในบริการของบริษัทอื่นและบันทึก SQL คุณต้องแทรก requestId ลงในบริบทด้วย requestId จะถูกส่งผ่านไปยังฟังก์ชันที่เกี่ยวข้องสำหรับการพิมพ์ การส่งผ่านทีละเลเยอร์นั้นยุ่งยากเกินไป และโค้ดก็ค่อนข้างรบกวนเช่นกัน
เป้าหมายของเราคือลดการบุกรุกโค้ด แทรกโค้ดเพียงครั้งเดียว และติดตามโค้ดโดยอัตโนมัติ
หลังจากการวิจัย async_hooks สามารถติดตามวงจรชีวิตของพฤติกรรมแบบอะซิงโครนัสได้ ในแต่ละทรัพยากรแบบอะซิงโครนัส (แต่ละคำขอเป็นทรัพยากรแบบอะซิงโครนัส) โดยมี 2 รหัส
ได้แก่ asyncId (ID วงจรชีวิตปัจจุบันของทรัพยากรแบบอะซิงโครนัส) trigerAsyncId (ทรัพยากรแบบอะซิงโครนัสหลัก) บัตรประจำตัว)
async_hooks จัดเตรียม hooks วงจรชีวิตต่อไปนี้เพื่อฟังทรัพยากรแบบอะซิงโครนัส:
asyncHook = async_hook.createHook({ //ฟังการสร้างทรัพยากรแบบอะซิงโครนัส init(asyncId,type,triggerAsyncId,resource){} // ก่อนที่ฟังก์ชันการเรียกกลับทรัพยากรแบบอะซิงโครนัสจะเริ่มดำเนินการก่อน (asyncId){} //หลังจากที่ฟังก์ชันเรียกกลับทรัพยากรแบบอะซิงโครนัสเริ่มดำเนินการ หลังจาก(asyncId){} // ตรวจสอบการทำลายทรัพยากรแบบอะซิงโครนัส destroy(asyncId){} })
จากนั้นหากเราทำการแมป แต่ละ asyncId จะแมปกับที่เก็บข้อมูล และ requestId ที่เกี่ยวข้องจะถูกจัดเก็บไว้ในที่เก็บข้อมูล ดังนั้น requestId ก็สามารถรับได้อย่างง่ายดาย
มันเพิ่งเกิดขึ้นที่ไลบรารี cls-hooked ได้รับการห่อหุ้มตาม async_hooks โดยคงสำเนาของข้อมูลไว้ในทรัพยากรแบบอะซิงโครนัสเดียวกันและจัดเก็บไว้ในรูปแบบของคู่คีย์-ค่า (หมายเหตุ: จำเป็นต้องใช้ async_hooked ในเวอร์ชันที่สูงกว่าของโหนด>=8.2.1) แน่นอนว่ายังมีการใช้งานอื่นๆ ในชุมชน เช่น cls-session, node-continuation-local-storage เป็นต้น
มาพูดถึงตัวอย่างของการใช้ cls-hooked ในโปรเจ็กต์ของฉัน:
/session.js สร้างพื้นที่เก็บข้อมูลที่มีชื่อ
const createNamespace = need('cls-hooked').createNamespace const session = createNamespace('requestId-store') module.exports = session
/logger.js บันทึกการพิมพ์
const session = need('./session') โมดูล.ส่งออก = { ข้อมูล: (ข้อความ) => - const requestId = session.get('requestId') console.log(`requestId:${requestId}`, ข้อความ) - ข้อผิดพลาด: (ข้อความ) => - const requestId = session.get('requestId') console.error(`requestId:${requestId}`, ข้อความ) - }
/sequelize.js sql เรียก logger เพื่อพิมพ์บันทึก
const logger = need("./logger") ใหม่ Sequelize( การบันทึก: ฟังก์ชั่น (sql, costtime) { logger.error( `sql exe : ${sql} | ต้นทุนเวลา ${costtime} ms` ); } )
/app.js ตั้งค่า requestId ตั้งค่า requestId เพื่อส่งคืนส่วนหัวการตอบกลับ และบันทึกการเข้าถึงการพิมพ์
const session = need('./session') const logger = ต้องการ('./logger') ฟังก์ชัน async accessHandler (ctx ถัดไป) - const requestId = ctx.header['x-request-id'] ||. const params = ctx.request.body ? JSON.stringify(ctx.request.body) : JSON.stringify(ctx.request.query) //ตั้งค่า requestId session.run(() => { session.set('requestId', requestId) logger.info(`url:${ctx.request.path}; params:${params}`) ถัดไป() //ตั้งค่าส่วนหัวการตอบกลับ ctx.res.setHeader('X-Request-Id',requestId) }) }
ลองดูบันทึกเมื่อเส้นทางคำขอเป็น /home?a=1:
บันทึกการเข้าถึง: requestId:79f422a6-6151-4bfd-93ca-3c6f892fb9ac url:/home;params:{"a": "1"} บันทึก SQL: รหัสคำขอ: 79f422a6-6151-4bfd-93ca-3c6f892fb9ac sql exe: ดำเนินการแล้ว (ค่าเริ่มต้น): SELECT `id` FROM t_user
คุณจะเห็นว่ารหัสคำขอบันทึกของลิงก์ทั้งหมดสำหรับคำขอเดียวกันนั้นเหมือนกัน หากมีการแจ้งเตือนที่ส่งไปยังแพลตฟอร์มการแจ้งเตือนในภายหลัง เราจะสามารถค้นหาลิงก์ทั้งหมดที่ดำเนินการโดยคำขอนี้โดยยึดตาม requestId
นักเรียนที่ระมัดระวังอาจสังเกตเห็นว่าฉันยังตั้งค่า requestId ในส่วนหัวการตอบกลับที่ส่งคืนโดยอินเทอร์เฟซ จุดประสงค์คือหากพบว่าคำขอตอบสนองช้าหรือมีปัญหา สามารถทราบ requestId ได้โดยตรงจากเบราว์เซอร์และวิเคราะห์
ฉันทำการทดสอบความเครียดในเครื่อง
นี่คือการเปรียบเทียบการใช้หน่วยความจำ:
มากกว่าการไม่ใช้ async_hook ประมาณ 10%
ไม่เป็นไรสำหรับระบบ QPS ของเราที่มีระบบ 100 ระดับ แต่หากเป็นบริการที่เกิดขึ้นพร้อมกันสูง เราอาจจะต้องพิจารณาอย่างรอบคอบ
PS. หากมีข้อผิดพลาดประการใด โปรดชี้แนะ หากไม่ชอบ โปรดอย่าแสดงความคิดเห็น