背景
在上一篇文章中提到了如何部署企业级Node.js应用性能监控(Easy-Monitor篇)之后,偶然之间在极客时间里看到了作者分享的一个视频,详细介绍了下该项目的架构设计和实现,讲的特别好,爱不释手,我就在这里做一个笔记,方便之后再遇到类似项目设计时翻阅参考。
痛点
线上故障处理,故障定位一体化
- 采集 Status、CPU、Memory、Pid
- 采集 Heap Statistics、Heap Space、Active Handles、Active Requests
- 更多内核数据
- 应用的 GC 状态是否健康
- Libuv 的每一个活跃的句柄详情,类似于 setTimeout
- 再任意状况下都能对进城进行采样/导出快照
理想的性能监控方案
- 内核数据:基本采集展示
- 运维告警:异常感知机制
- 实时诊断:故障排查能力
解法
Node.js Addon: 用于扩展 Node.js 的功能机制,允许开发者使用 C/C++ 编写本机模块,特别适合用于需要大量计算、访问底层系统资源或与其他 C/C++ 代码集成的场景
监控插件(Addon)
根据配置添加致命错误钩子:在线程 Crash 时可以输出日志。
根据配置 Monkey Patch:Patch HTTP 模块,用于记录请求的响应时间。
内核数据采集线程:
接收采样指令线程:
堆快照和 Heap 采样区别?
如果在采样时仍然存在内存泄露,则使用 Heap 采样可以方便找出问题,否则需要使用堆快照来处理。
为什么采用 IPC Server 而不是 TCP Service?
TCP Service 是跨终端通信,需要占用端口。
为什么采用 RequestInterrupt/uv_async_send?
无论 JS 工作线程处于什么状态,比如卡死了或者不能再工作了,都能执行 Profiling 动作。
为什么不选择直接基于 Node.js Runtime 去改,类似于 Alinode? 功能更加强大,但维护成本高,每次 Node.js 升级都需要修改原有代码。
插件模块是直接上报好还是采集/控制分离好?
采集/控制分离更好,稳定性更高,采集服务挂了,但业务线程不受影响,也不会出现和业务无关的异常日志
监控服务端架构
为什么要把日志处理与存储服务和 Agent 长连接服务分开?
在线上实例非常多,比如 10 万级别,如果没有分开,一旦日志处理与存储服务功能迭代,重新部署时会导致长连接重连,进一步会导致日志服务端雪崩效益,哪怕可以通过随机连接来减少影响,但仍然会降低日志服务的稳定和可用性。而通过将其分离,并且把 Agent 长连接服务逻辑尽可能轻,则可以规避该影响。
用户控制台
鉴权模块和团队协作
告警规则
可视化
完整的应用故障定位步骤
优势
- 用户简单易用
- 仅需在 Node.js 应用入口引入插件模块
- 低侵入性,无需更改现有业务代码
- 监控功能完备
- 针对Node.js 应用指标的性能监控
- 灵活可配置的自定义运维阈值告警
- 可实时导出的 Runtime 采样与快照
使用案例
内存泄露
使用堆快照,找到 Retained Size 最大的地方,Retainders 可以看到父引用持有点是谁。
进程卡死
CPU 打满,使用诊断报告(也可以用 CPU Profile)打出实时 JS 堆栈。