-
-
Save pjchender/3a15ef430b99e248af274845d9c10172 to your computer and use it in GitHub Desktop.
Node.js and CPU profiling on production (in real-time without downtime)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Simple demo of how to integrate in app.js | |
const inspectorSocket = require('./sockets/inspectorSocket'); | |
const io = require('socket.io')(server); | |
app.set('socketio', io); | |
inspectorSocket(io); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Reference: Node.js and CPU profiling on production (in real-time without downtime) | |
* https://medium.com/voodoo-engineering/node-js-and-cpu-profiling-on-production-in-real-time-without-downtime-d6e62af173e2 | |
**/ | |
const inspector = require('inspector'); | |
const session = new inspector.Session(); | |
const { uploadFile2GCS } = require('./../google/storage'); | |
session.connect(); | |
let timer; | |
// 每此存檔後便開始紀錄下一次的 Profile | |
const saveCPUProfile = socket => { | |
session.post('Profiler.stop', async (err, { profile }) => { | |
if (!err) { | |
// 直接將檔案上傳到 Google Cloud Storage | |
const filename = `profile-${Date.now()}.cpuprofile`; | |
await uploadFile2GCS({ | |
data: JSON.stringify(profile), | |
filename, | |
}); | |
socket.emit('message', `file uploaded - ${filename}`); | |
session.post('Profiler.start'); | |
} | |
}); | |
}; | |
// 開始啟動監控 | |
const startInspect = socket => { | |
socket.emit('message', 'start inspect'); | |
// 啟動 Profiler 並開始監控 | |
session.post('Profiler.enable', () => { | |
session.post('Profiler.start'); | |
}); | |
// 每分鐘會進行一次寫檔 | |
return setInterval(() => saveCPUProfile(socket), 1 * 60 * 1000); | |
}; | |
// 停止繼續監控 | |
const stopInspect = socket => { | |
socket.emit('message', 'stop inspect'); | |
clearInterval(timer); | |
timer = null; | |
}; | |
export default io => { | |
// 透過 socket 來啟動或停止監控 | |
io.of('/inspector').on('connection', socket => { | |
socket.on('message', payload => { | |
const { key, msg } = payload; | |
if (key !== '50868012') { | |
console.log('Invalid inspector key'); | |
return; | |
} | |
switch (msg) { | |
case 'startInspect': { | |
if (timer) { | |
socket.emit( | |
'message', | |
'some inspector is still running, stop it before invoke other inspector... ' | |
); | |
return; | |
} | |
timer = startInspect(socket); | |
break; | |
} | |
case 'stopInspect': { | |
stopInspect(socket); | |
break; | |
} | |
default: | |
stopInspect(socket); | |
} | |
}); | |
// socket.on('disconnect', () => { | |
// stopInspect(socket); | |
// }); | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Storage } from '@google-cloud/storage'; | |
const storage = new Storage({ | |
keyFilename: '<credential>.json', | |
}); | |
const BUCKET_NAME = 'jubo-demo'; | |
const bucket = storage.bucket(BUCKET_NAME); | |
const uploadFile2GCS = ({ data, filename }) => | |
new Promise((resolve, reject) => { | |
const file = bucket.file(filename); | |
const stream = file.createWriteStream({ | |
contentType: 'application/octet-stream', | |
}); | |
stream.on('error', (err) => { | |
reject(err); | |
}); | |
stream.on('finish', () => { | |
resolve(`https://storage.googleapis.com/${BUCKET_NAME}/${filename}`); | |
}); | |
stream.end(data); | |
}); | |
export { storage, uploadFile2GCS }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment