监控Nodejs的性能实例代码


Posted in NodeJs onJuly 02, 2019

下面给大家介绍下监控Nodejs的性能,

最近想监控一下Nodejs的性能。记录分析Log太麻烦,最简单的方式是记录每个HTTP请求的处理时间,直接在HTTP Response Header中返回。

记录HTTP请求的时间很简单,就是收到请求记一个时间戳,响应请求的时候再记一个时间戳,两个时间戳之差就是处理时间。

但是,res.send()代码遍布各个js文件,总不能把每个URL处理函数都改一遍吧。

正确的思路是用middleware实现。但是Nodejs没有任何拦截res.send()的方法,怎么破?

其实只要稍微转换一下思路,放弃传统的OOP方式,以函数对象看待res.send(),我们就可以先保存原始的处理函数res.send,再用自己的处理函数替换res.send:

app.use(function (req, res, next) {
  // 记录start time:
  var exec_start_at = Date.now();
  // 保存原始处理函数:
  var _send = res.send;
  // 绑定我们自己的处理函数:
  res.send = function () {
    // 发送Header:
    res.set('X-Execution-Time', String(Date.now() - exec_start_at));
    // 调用原始处理函数:
    return _send.apply(res, arguments);
  };
  next();
});‘

只用了几行代码,就把时间戳搞定了。

对于res.render()方法不需要处理,因为res.render()内部调用了res.send()。

调用apply()函数时,传入res对象很重要,否则原始的处理函数的this指向undefined直接导致出错。

实测首页响应时间9毫秒:

x-execution-time

监控Nodejs的性能实例代码

ps:下面给大家介绍下nodejs实现远程桌面监控的方法,具体内容如下所示:

最近使用node实现了一个远程桌面监控的应用,分为服务端和客户端,客户端可以实时监控服务端的桌面,并且可以通过鼠标和键盘来控制服务端的桌面。

监控Nodejs的性能实例代码

监控Nodejs的性能实例代码

这里因为我是用的同一台电脑,所以监控画面是这样的,当然使用两台电脑一个跑 客户端 ,一个跑 服务端 才有意义。

原理

其实这个应用的功能主要分为两部分,一是实现监控,即在客户端可以看到服务端的桌面,这部分功能是通过定时截图来实现的,比如服务端一秒截几次图,然后通过 socketio 发送到客户端,客户端通过改变img的src来实现一帧帧的显示最新的图片,这样就能看到动态的桌面了。监控就是这样实现的。

另一个功能是控制,即客户端对监控画面的操作,包括鼠标和键盘的操作都可以在服务端的桌面真正的生效,这部分功能的实现是在electron的应用中监听了所有的鼠标和键盘事件,比如keydown、keyup、keypress,mousedown、mouseup、mousemove、click等,然后通过socketio把事件传递到服务端,服务端通过 robot-js 来执行不同的事件,这样就能使得客户端的事件在服务端触发了。

实现

原理讲完,我们来具体实现一下( 源码链接在这 )。

实现socket通信

首先,服务端和客户端分别引入 socket.io 和 socket.io-client , 分别初始化

服务端:

const app = new Koa();
const server = http.createServer(app.callback());
createSocketIO(server);

app.use((ctx): void => {
 ctx.body = 'please connect use socket';
});

server.listen(port, (): void => {
 console.log('server started at http://localhost:' + port);
});
//createSocketIO
const io = socketIO(server, {
 pingInterval: 10000,
 pingTimeout: 5000,
 cookie: false
 });
io.on('connect', (socket): void => {
 socket.emit('msg', 'connected');
}

客户端:

var socket = this.socket = io('http://' + this.ip + ':3000')
socket.on('msg', (msg) => {
 console.log(msg)
})
socket.on('error', (err) => {
 alert('出错了' + err)
})

这样,服务端和客户端就通过socketio建立了链接。

实现桌面监控

之后我们首先要在服务端来截图,使用 screenshot-desktop 这个包

const screenshot = require('screenshot-desktop')

const SCREENSHOT_INTERVAL = 500;

export const createScreenshot = (): Promise<[string, Buffer]> => {
 return screenshot({format: 'png'}).then((img): [string, Buffer] => {
 return [ img.toString('base64'), img];
 }).catch((err): {} => {
 console.log('截图失败', err);
 return err;
 })
}

export const startScreenshotTimer = (callback): {} => {
 return setInterval((): void => {
 createScreenshot().then(([imgStr, img]): void => {
  callback(['data:image/png;base64,' + imgStr, img]);
 })
 }, SCREENSHOT_INTERVAL)
}

然后通过socketio的emit来传到客户端:

startScreenshotTimer(([imgStr, img]): void => {
 io.sockets.emit('screenshot', imgStr);
});

客户端收到图片后,设置到img的src上(这里是base64的图片url):

<img 
 class="screenshot" 
 :src="screenshot"
/>

data () {
 return {
 screenshot: ''
 }
}

socket.on('screenshot', (data) => {
 this.screenshot = data
})

其实这样就已经实现了桌面监控了,有兴趣的同学可以照着这个思路实现看看,并不是很麻烦。

当然这样的方案是有问题的,因为我们需要知道服务端桌面尺寸的大小,然后根据这个来调整客户端显示的图片尺寸。

实现这个细节是使用的 get-pixels 这个库,可以读取本地图片文件的宽度高度等信息,所以我先把图片写入本地,然后又读取出来,这样获取到的屏幕尺寸。

interface ScreenSize {
 width: number;
 height: number;
}

function getScreenSize(img): Promise<ScreenSize> {
 const imgPath = path.resolve(process.cwd(), './tmp.png');
 fs.writeFileSync(imgPath, img);
 return new Promise((resolve): void => {
 getPixels(imgPath, function(err, pixels): void {
  if(err) {
  console.log("Bad image path")
  return
  }
  resolve({
  width: pixels.shape[0],
  height: pixels.shape[1]
  });
 });
 })
}

然后通过socektio传递给客户端

getScreenSize(img).then(({ width, height}) => {
 io.sockets.emit('screensize', {
 width,
 height
 })
});

客户端收到之后调整图片大小就可以了

<img 
 class="screenshot" 
 :src="screenshot"
 :style="screenshotStyle"
/>

data () {
 return {
 screenshot: '',
 screenshotStyle: '',
 }
}

socket.on('screensize', (screensize) => {
 this.screenshotStyle = {'width': screensize.width + 'px', 'height': screensize.height + 'px'}
})

至此已经实现了桌面监控,并且图片尺寸和服务端屏幕的尺寸是一致的。

这里还有一个细节,就是获取到的图片大小是物理像素,而客户端设置的px是设备无关像素,也就是要除以dpr才是px的值。这里需要获取dpr,因为目前只是在mac下用,所以直接除以2了。

实现远程控制

代码写到这里,客户端的electron应用中已经可以实时显示服务端的桌面了。(当然像输入ip的弹框,以及electron-vue和typescript等和主要逻辑无关的细节就不展开了。)

接下来我们要实现远程控制,也就是监听事件,传递事件,执行事件这几部分。

首先我们定义一下传递的事件的格式:

interface MouseEvent {
 type: string;
 buttonType: string;
 x: number;
 y: number;
}

interface KeyboardEvent {
 type: string;
 keyCode: number;
 keyName: string;
}

鼠标事件MouseEvent,type为鼠标事件的类型,具体的值包括mousedown、mouseup、mousemove、click、dblclick,buttonType指的是鼠标的左键还是右键,值为 left 或 right,x和y是具体的坐标。

键盘事件KeyboardEvent,type为键盘事件的类型,具体的值包括keydown、keyup、keypress,keyCode为键盘码,keyName为键的名字。

接下来我们要在客户端监听事件:

<img 
 class="screenshot" 
 :src="screenshot"
 :style="screenshotStyle"
 @mousedown="handleMouseEvent"
 @mousemove="handleMouseEvent" 
 @mouseup="handleMouseEvent"
 @click="handleMouseEvent"
 @dblclick="handleMouseEvent" 
/>

window.onkeypress = window.onkeyup = window.onkeydown = this.handleKeyboardEvent

通过socekt把事件传递到服务端

handleKeyboardEvent (e) {
 this.socket && this.socket.emit('userevent', {
 type: 'keyboard',
 event: {
 type: e.type,
 keyName: e.key,
 keyCode: e.keyCode
 }
 })
 },
 handleMouseEvent (e) {
 this.socket && this.socket.emit('userevent', {
 type: 'mouse',
 event: {
 type: e.type,
 buttonType: e.buttons === 2 ? 'right' : 'left',
 x: e.clientX,
 y: e.clientY
 }
 })
 },

然后在服务端把事件取出来执行,执行事件使用的是 robot-js :

const { Mouse, Point, Keyboard } = require('robot-js');

interface MouseEvent {
 type: string;
 buttonType: string;
 x: number;
 y: number;
}

interface KeyboardEvent {
 type: string;
 keyCode: number;
 keyName: string;
}

export default class EventExecuter {
 public mouse;
 public keyboard;
 public constructor(){
 this.mouse = new Mouse();
 this.keyboard = new Keyboard();
 }

 public executeKeyboardEvent(event: KeyboardEvent): void {
 switch(event.type) {
  case 'keydown':
  this.keyboard.press(event.keyCode);
  break;
  case 'keyup':
  this.keyboard.release(event.keyCode);
  break;
  case 'keypress':
  this.keyboard.click(event.keyCode);
  break;
  default: break;
 }
 }

 public executeMouseEvent(event): void {
 Mouse.setPos(new Point(event.x, event.y));
 const button = event.buttonType === 'left' ? 0 : 2
 switch(event.type) {
  case 'mousedown':
  this.mouse.press(button);
  break;
  case 'mousemove':
  break;
  case 'mouseup': 
  this.mouse.release(button);
  break;
  case 'click': 
  this.mouse.click(button);
  break;
  case 'dblclick': 
  this.mouse.click(button);
  this.mouse.click(button);
  break;
  default: break;
 }
 }

 public exectue(eventInfo): void {
 console.log(eventInfo);
 switch (eventInfo.type) {
  case 'keyboard':
  this.executeKeyboardEvent(eventInfo.event);
  break;
  case 'mouse':
  this.executeMouseEvent(eventInfo.event);
  break;
  default: break;
 }
 }
}

至此,桌面监控和远程控制的客户端还有服务端的部分,以及两端的通信都已经实现了。思路其实并不麻烦,但细节还是很多的。有兴趣的同学可以把代码下下来跑跑试试,或者按着这个思路自己实现一遍,还是挺好玩的。

总结

以上所述是小编给大家介绍的nodejs实现远程桌面监控的方法,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

NodeJs 相关文章推荐
nodejs下打包模块archiver详解
Dec 03 NodeJs
NodeJS学习笔记之Connect中间件模块(二)
Jan 27 NodeJs
NodeJs的优势和适合开发的程序
Aug 14 NodeJs
详解nodeJS中读写文件方法的区别
Mar 06 NodeJs
nodejs模块nodemailer基本使用-邮件发送示例(支持附件)
Mar 28 NodeJs
nodejs socket实现的服务端和客户端功能示例
Jun 02 NodeJs
详解HTTPS 的原理和 NodeJS 的实现
Jul 04 NodeJs
NodeJS简单实现WebSocket功能示例
Feb 10 NodeJs
Nodejs中的JWT和Session的使用
Aug 21 NodeJs
使用nodejs实现JSON文件自动转Excel的工具(推荐)
Jun 24 NodeJs
Nodejs实现微信分账的示例代码
Jan 19 NodeJs
搭建一个nodejs脚手架的方法步骤
Jun 28 #NodeJs
独立部署小程序基于nodejs的服务器过程详解
Jun 24 #NodeJs
nodejs实现获取本地文件夹下图片信息功能示例
Jun 22 #NodeJs
NodeJs 模仿SIP话机注册的方法
Jun 21 #NodeJs
通过Nodejs搭建网站简单实现注册登录流程
Jun 14 #NodeJs
NodeJs生成sitemap站点地图的方法示例
Jun 11 #NodeJs
nodejs提示:cross-device link not permitted, rename错误的解决方法
Jun 10 #NodeJs
You might like
手冲咖啡应该是现代精品咖啡店的必备选项吗?
2021/03/03 冲泡冲煮
JS类中定义原型方法的两种实现的区别
2007/03/08 Javascript
替代window.event.srcElement效果的可兼容性的函数
2009/12/18 Javascript
23个超流行的jQuery相册插件整理分享
2011/04/25 Javascript
EasyUI的treegrid组件动态加载数据问题的解决办法
2011/12/11 Javascript
caller和callee的区别介绍及演示结果
2013/03/10 Javascript
点击进行复制的JS代码实例
2013/08/23 Javascript
常见表单重复提交问题整理及解决方法
2013/11/13 Javascript
使用jquery animate创建平滑滚动效果(可以是到顶部、到底部或指定地方)
2014/05/27 Javascript
javascript显式类型转换实例分析
2015/04/25 Javascript
AngularJS模块管理问题的非常规处理方法
2015/04/29 Javascript
jquery实现页面常用的返回顶部效果
2016/03/04 Javascript
Google 地图类型详解及示例代码
2016/08/06 Javascript
微信小程序 数据访问实例详解
2016/10/08 Javascript
JS插件plupload.js实现多图上传并显示进度条
2016/11/29 Javascript
浅谈node模块与npm包管理工具
2018/01/03 Javascript
Vue+webpack项目配置便于维护的目录结构教程详解
2018/10/14 Javascript
JS中通过url动态获取图片大小的方法小结(两种方法)
2018/10/31 Javascript
在Angular中实现一个级联效果的下拉框的示例代码
2020/05/20 Javascript
使用PM2+nginx部署python项目的方法示例
2018/11/07 Python
python3文件复制、延迟文件复制任务的实现方法
2019/09/02 Python
python sklearn包——混淆矩阵、分类报告等自动生成方式
2020/02/28 Python
pytorch快速搭建神经网络_Sequential操作
2020/06/17 Python
Flask-SocketIO服务端安装及使用代码示例
2020/11/26 Python
python 装饰器重要在哪
2021/02/14 Python
详解python3 GUI刷屏器(附源码)
2021/02/18 Python
狗狗玩具、零食和咀嚼物的月度送货服务:Super Chewer
2018/08/22 全球购物
美国专业消费电子及摄影器材网站:B&H Photo Video
2019/12/18 全球购物
网络教育毕业生自我鉴定
2013/10/10 职场文书
工商学院毕业生自荐信
2013/11/12 职场文书
初中三年学生的学习自我评价
2013/11/13 职场文书
小学生获奖感言范文
2014/02/02 职场文书
农林环境专业求职信
2014/03/13 职场文书
年终总结会主持词
2014/03/25 职场文书
差生评语大全
2014/05/04 职场文书
担保书格式范文
2015/09/22 职场文书