Skip to content

Instantly share code, notes, and snippets.

@pjchender

pjchender/app.js Secret

Last active June 15, 2020 01:41
Show Gist options
  • Save pjchender/3a15ef430b99e248af274845d9c10172 to your computer and use it in GitHub Desktop.
Save pjchender/3a15ef430b99e248af274845d9c10172 to your computer and use it in GitHub Desktop.
Node.js and CPU profiling on production (in real-time without downtime)
// 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);
/**
* 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);
// });
});
};
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