监控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与Mysql的交互示例代码
Aug 18 NodeJs
nodejs文件操作模块FS(File System)常用函数简明总结
Jun 05 NodeJs
nodejs教程之入门
Nov 21 NodeJs
Nodejs中的this详解
Mar 26 NodeJs
利用nodejs监控文件变化并使用sftp上传到服务器
Feb 18 NodeJs
nodejs处理图片的中间件node-images详解
May 08 NodeJs
NodeJs使用Mysql模块实现事务处理实例
May 31 NodeJs
nodejs密码加密中生成随机数的实例代码
Jul 17 NodeJs
nodejs之koa2请求示例(GET,POST)
Aug 07 NodeJs
nodejs基础之常用工具模块util用法分析
Dec 26 NodeJs
nodejs微信开发之接入指南
Mar 17 NodeJs
nodejs中内置模块fs,path常见的用法说明
Nov 07 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
PHPCMS忘记后台密码的解决办法
2016/10/30 PHP
php gethostbyname获取域名ip地址函数详解
2010/01/24 Javascript
html+javascript实现可拖动可提交的弹出层对话框效果
2013/08/05 Javascript
JavaScript常用验证函数实例汇总
2014/11/25 Javascript
jquery隔行换色效果实现方法
2015/01/15 Javascript
快速学习jQuery插件 Cookie插件使用方法
2015/12/01 Javascript
借助FileReader实现将文件编码为Base64后通过AJAX上传
2015/12/24 Javascript
JSON字符串转换JSONObject和JSONArray的方法
2016/06/03 Javascript
Bootstrap fileinput文件上传预览插件使用详解
2017/05/16 Javascript
vue系列之动态路由详解【原创】
2017/09/10 Javascript
使用webpack3.0配置webpack-dev-server教程
2018/05/29 Javascript
JS实现图片切换特效
2019/12/23 Javascript
微信浏览器左上角返回按钮监听的实现
2020/03/04 Javascript
实现一个Vue自定义指令懒加载的方法示例
2020/06/04 Javascript
vue中实现拖动调整左右两侧div的宽度的示例代码
2020/07/22 Javascript
在vs code 中如何创建一个自己的 Vue 模板代码
2020/11/10 Javascript
Python 列表list使用介绍
2014/11/30 Python
Python中MySQLdb和torndb模块对MySQL的断连问题处理
2015/11/09 Python
python+pillow绘制矩阵盖尔圆简单实例
2018/01/16 Python
Numpy 将二维图像矩阵转换为一维向量的方法
2018/06/05 Python
Python基于sklearn库的分类算法简单应用示例
2018/07/09 Python
python 美化输出信息的实例
2018/10/15 Python
详解如何用django实现redirect的几种方法总结
2018/11/22 Python
Python并发:多线程与多进程的详解
2019/01/24 Python
使用HTML5做个画图板的方法介绍
2013/05/03 HTML / CSS
2013年保送生自荐信格式
2013/11/20 职场文书
赔偿协议书范本
2014/04/15 职场文书
乡镇群众路线教育实践活动整改措施
2014/10/04 职场文书
群众路线剖析材料怎么写
2014/10/09 职场文书
2014年初级职称工作总结
2014/12/08 职场文书
大学同学聚会感言
2015/07/30 职场文书
民事调解协议书
2016/03/21 职场文书
postgres之jsonb属性的使用操作
2021/06/23 PostgreSQL
CSS实现章节添加自增序号的方法
2021/06/23 HTML / CSS
浅析Django接口版本控制
2021/06/26 Python
python中if和elif的区别介绍
2021/11/07 Python