easy-monitor源码分析

前言

笔者之前有使用轻量级的easy-monitor2.0对项目进行内存泄漏的排查;

本文主要是对2.0版本的源码学习和笔记整理。目的是为了个人的技术提升,想去了解一个nodejs监控的整体实现。如果哪里有理解不对的地方欢迎读者指出。

本文底部会有原作者在cnode原文章的链接;

整体架构

这里引用一下原文中的架构图


image.png

整体上分为了三个模块:

  • 业务进程中运行的embrace模块
  • dashboard看板服务模块
  • web页面

下面从源码层面对各模块进行分析;

初始化

这个库对外暴露的是一个方法。

'use strict';
const easyMonitor = require('easy-monitor');
easyMonitor('Mercury');
const express = require('express');
const app = express();

app.get('/hello', function (req, res, next) {
    res.send('hello');
});

app.listen(8082);

入口文件index.js引入根目录的dispatch.js

dispatch.png

这个文件主要做了以下工作:

  • src_logic/common目录的js文件进行初始化,导出一个对象;收敛了common目录下的所有模块方法。并且遍历该对象属性值,如果包含initP方法就进行调用;
//获取基础配置, pre 表示预先加载的文件,params 表示对应的参数
const common = _common({ pre: ['config', 'logger', 'utils', 'cache'], param: { config: options } });
yield common.utils.commonInitP(common);
  • src_logic/common目录下的每一个模块暴露的都是一个初始化的方法,接受的参数是一致的,其实对应的就是common目录被前置加载的模块;
function (common, config, logger, utils, cache) { ... }
  • 在初始化上述config模块时,使用了类似的初始化过程对src_logic/config目录下的所有配置文件进行了初始化;并导出了一个对象,收敛了所有配置选项;
    详细过程阅读src_logic/common/common.config.js模块

  • 初始话完毕后便是运行embrace模块的start方法
    通过fork子进程形式运行dashboard模块
    (没有分析集群部署模式)

//非 cluster 模式下,embrace 嵌入业务进程,dashboard 以 fork 形式启动
embrace.start(config, common);
common.utils.forkNode(path.join(rootPath, 'dashboard/_fork.js'), [JSON.stringify(options)]);

关于RPC

这里插入一下RPC的概念,方便理解下面的embracedashboard之间的通信

这里引用一段网络上的解释:原文
RPC (Remote Procedure Call:远程过程调用):一种进程间通信方式。允许像调用本地服务一样调用远程服务

RPC架构:
包含四个核心组件

  • 客户端(client):服务的调用方
  • 服务端(server):服务提供方
  • 客户端存根(client stub):将客户端请求参数打包成网络消息,再发给服务方
  • 服务端存根(server stub):接收客户端发来的消息,将消息解包,并调用本地方法
image.png

其实源码中embrace模块对应的就是Clientdashboard模块对应的就是Server,基于TCP链接实现的通信;

embrace模块

embrace模块暴露出的start方法将上文中初始化后完整的config对象和common对象作为了参数传入;

embrace.png

这里主要进行的工作是:

  • 加载embrace/dispatch模块
  • 加载所有embrace/controller目录下的逻辑处理方法 ,集成到一个controller对象上
//获取 embrace 的 dispatch 信息
const dispatch = _require('embrace/dispatch');
const controller = dispatch.controller(config, common, dbl);
  • 启动tcp客户端服务;(注:与dashboard模块通信的客户端)
    并设置this指向 { controller }
//启动 tcp 客户端
const tcpClient = dispatch.tcp;
tcpClient.apply({ controller }, [config, common, dbl]);

embrace/dispatch模块

上文中引入的embrace/dispatch模块对外暴露了上文中用到的两个方法

  • createTcpClient:启动tcp客户端服务
    这里使用了net.Socket类创建了socket实例,并且调用了实例方法connect,以tcp模式进行链接。
//和服务器建立链接
const client = new net.Socket();
client.connect(config.embrace.tcp_port, config.embrace.tcp_host, _callbackListener);
//处理 tcp 数据
client.on('data', socketUtils.onData.bind(ctx, client));

这里的socketUtils.onDatacommon/common.scoket.js模块中暴露出的方法,目的是为了统一处理tcp句柄中的 data 事件;
dashboard模块中创建的tcp服务端的回调函数中会再次用到该方法;目的是对相同格式的消息数据格式统一处理;

  • createController:集成controller方法
    controller有4种类型: auth、fetch、overview、profiler,对应四种逻辑处理;

dashboard模块

dashboard模块初始化和启动的逻辑与embrace模块异曲同工,稍有不同的是区分为了两部分:

  • HTTP服务:处理web端用户的操作,对TCP服务下发指令
  • TCP服务端: 处理embrace客户端发来的消息内容,通知embrace客户端开始对应的操作

overview

这里以overview为例子走一遍完整的流程

首页

image.png

overview页面:此页面可以看到服务器的 CPU 使用率,以及被选中进程的 Memory 占用情况,其中内存占用展示了三类:

  • heapUsed: 正在使用的堆内内存大小
  • heapTotal: 申请的总堆内内存大小
  • rss: 堆外分配的内存大小
image.png

整个过程笔者使用了一个简易的流程图来示意;(如果不容易理解,请还参考原作者的架构图)


image.png
  • web发送 fetchOverview请求
  • dashboard http服务 接收到请求后,检查缓存是否有内容;并且会通知 TCP server 发送消息给 TCP client;最后将消息响应发送回web;
  • TCP client 解析消息调用对应controller获取当前进程cpu占用率以及内存使用情况并写入缓存,供下次dashboard http服务去读取信息

上述过程在web端是启动了setInterval定时器每一秒中请求一次,而且在客户端每一次都将上一次的数据做了缓存,绘制出了实时的折线图效果

具体获取cpumem数据是通过下面的方法:

//获取本进程的 cpu 使用率和 memory 占用信息
const memoryUsage = common.overview.computeMemoryUsage();
const cpuUsage = common.overview.computeCpuUsage();

computeMemoryUsage的计算方法比较直接:process_memoryusage

mem.png

computeMemoryUsage使用到的Nodejs API:os_cpus并进行了简单计算

cpu.png

CPU 数据采集分析函数运算瓶颈

image.png

这个功能是对CPU-Profiling,然后进行分析,展示结果包含:

  • 执行耗费时间大于 500ms(默认值) 的函数列表
  • 执行耗费时间最长的 5个(默认值) 函数
  • V8 引擎逆优化最频繁的 5个(默认值) 函数

感谢大佬的开源精神
easy-monitor作者原文链接
easy-monitor3.0已经发布。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容