这是 Phoenix监控平台技术解析 系列博客的第一篇。本系列将从宏观到微观,由浅入深地拆解Phoenix这个开源监控平台的设计思路、技术选型与核心实现。本篇作为开篇,带你建立对整个项目的全局认知。
一、为什么需要一个监控平台?
在分布式系统日益普及的今天,一个中等规模的项目可能同时运行着数十个应用实例、若干台服务器、多种数据库、容器化服务以及各类网络设备。面对如此复杂的运行环境,以下痛点几乎无法回避:
- 黑盒运行:服务是否健康?CPU、内存是否即将耗尽?磁盘还剩多少?如果没有监控,一切只能靠猜。
- 故障后知后觉:用户反馈"系统挂了",你才开始排查——此时损失已经造成。
- 排查效率低:日志散落在各台机器上,JVM状态需要逐个连接jconsole查看,数据库慢SQL更是无从追溯。
- 告警能力缺失:即使发现了问题指标,也没有统一的通道把告警推送给相关人员。
Phoenix 就是为了解决这些问题而生的。它不是一个"大而全"的 APM 平台,而是一个实用、轻量、灵活可配置 的监控系统——既能监控基础设施(服务器、网络、Docker),也能监控应用程序(Java进程、JVM、线程池),还能对接多种告警通道。
二、Phoenix 能做什么?
在深入技术细节之前,先梳理 Phoenix 的监控能力边界:
| 监控维度 | 监控对象 | 具体内容 |
|---|---|---|
| 应用程序 | Java应用(默认) | 在线状态、JVM内存/线程/GC/类加载、线程池、业务告警、异常日志 |
| 服务器 | Linux/Windows/macOS/Unix | CPU、GPU、内存、磁盘、网卡、进程、平均负载、电池、传感器 |
| 网络设备 | 交换机/路由器(SNMP协议) | 在线状态、系统信息、接口信息 |
| Docker | Docker服务 | 服务概况、容器、镜像、事件、资源统计(CPU/内存/网络IO/磁盘IO) |
| 数据库 | MySQL/Oracle/Redis/Mongo | 会话、表空间、慢SQL、连接状态、数据库全量信息 |
| 网络 | ICMP Ping | 网络连通性、平均响应时间 |
| TCP | TCP端口 | 端口存活状态、响应时间 |
| HTTP | HTTP接口 | 接口可用性、响应时间、状态码 |
| 告警 | 多通道 | 电子邮件、钉钉、企业微信、飞书 |
值得注意的是,Phoenix 支持 HTTP 和 WebSocket 双通道通信。HTTP 是传统的请求-响应模式,WebSocket 则基于 Netty 实现长连接,适用于实时性要求更高的场景。由于对外暴露的是标准化接口协议,虽然默认客户端是 Java 实现,但只要遵循协议规范,任何语言都可以接入。
三、系统架构:四端协作
Phoenix 采用经典的四端分离架构,每个端各司其职:
3.1 phoenix-client:轻量级的应用监控SDK
客户端是需要集成到你的 Java 应用程序中的监控 SDK,接入成本极低——SpringBoot 项目只需引入一个 starter 依赖并加上
@EnableMonitoring 注解,并配置服务端地址即可,无需修改任何业务代码。它的职责是:
- 定时发送心跳包:默认每 30 秒一次,让服务端知道"我还活着"
- 采集JVM信息:内存使用、线程状态、GC情况、类加载数据
- 采集线程池信息:核心线程数、活跃线程、队列大小、拒绝策略等
- 上报服务器信息(可选):CPU、内存、磁盘等操作系统级指标
- 业务埋点:支持在代码中主动发送告警和异常信息
客户端提供了三种集成方式:
phoenix-client-core:适用于普通 Java 程序phoenix-client-spring-boot-starter:SpringBoot 项目一个注解即可接入phoenix-client-spring-mvc-integrator:适用于传统 SpringMVC 项目
3.2 phoenix-agent:基础设施的信息采集者
代理端部署在需要监控的服务器上,它承担着双重角色:
- 信息采集者:利用 oshi/Sigar 采集服务器硬件信息,利用 docker-java 采集 Docker 信息,利用 snmp4j 采集网络设备信息
- 信息中转站:汇聚并转发来自客户端的监控数据。如果部署在跳板机上,可以打通网络壁垒,让内网应用的监控数据也能抵达服务端
3.3 phoenix-server:监控平台的大脑
服务端是整个平台的核心枢纽,接收来自客户端和代理端的所有监控数据,并进行:
- 数据持久化:将各类监控信息写入 MySQL 数据库(当前+历史记录双表设计)
- 状态判定:基于心跳超时、Ping 不通、端口不可达等条件判定在线/离线
- 告警触发:通过 Quartz 定时任务周期性检测,发现异常时通过邮件、钉钉、企业微信、飞书推送告警
- 告警收敛:防止告警风暴,控制故障/恢复告警的发送频率
3.4 phoenix-ui:可视化操作台
UI 端是面向运维人员的 Web 管理界面,基于 SpringBoot + Thymeleaf + Layui 构建,提供:
- 首页仪表盘:总览所有监控对象的健康状态
- 各类监控详情页:服务器、应用、数据库、Docker、网络等
- 拓扑图:展示服务之间的链路关系
- 告警管理:告警记录查询、告警定义配置
- 监控配置:灵活配置监控频率、告警阈值、告警通道
- 用户管理:基于 SpringSecurity 的权限控制,支持自有认证和 CAS 第三方认证
四、工程模块结构
从 Maven 工程的角度看,Phoenix 采用多模块父子工程结构:
phoenix(父工程,统一管理依赖版本与构建配置)
├── phoenix-common(公共模块父工程)
│ ├── phoenix-common-core(核心公共模块:DTO、Domain、工具类、加解密、配置属性)
│ ├── phoenix-common-netty(Netty公共模块:WebSocket客户端/服务端封装)
│ └── phoenix-common-web(WEB公共模块:HTTP加解密切面、通用Web配置)
├── phoenix-client(客户端父工程)
│ ├── phoenix-client-core(客户端核心:Monitor入口、心跳/JVM/线程池定时任务)
│ ├── phoenix-client-spring-boot-starter(SpringBoot自动配置的Starter)
│ └── phoenix-client-spring-mvc-integrator(SpringMVC集成适配器)
├── phoenix-agent(代理端:服务器/Docker/网络设备信息采集、数据转发)
├── phoenix-server(服务端:数据接收、持久化、状态监测、告警引擎)
├── phoenix-ui(UI端:Web管理界面、SpringSecurity认证、ECharts图表)
├── doc(文档)
└── mvn(Maven打包脚本)
这种分层结构有几个明显的好处:
- 关注点分离:公共代码在 common 中复用,各端只关注自身业务
- 独立部署:agent、server、ui 分别打成可执行 JAR,支持独立扩缩容
- 客户端轻量:client 模块仅包含必要的上报逻辑,不引入服务端的重量级依赖
五、技术选型一览
| 层面 | 技术 | 说明 |
|---|---|---|
| 核心框架 | SpringBoot 2.3.x | 所有端的基座 |
| 通信层 | Netty (WebSocket) + HTTP | 双通道数据传输,WebSocket用于长连接实时通信 |
| 安全框架 | SpringSecurity + SpringSession | UI端认证鉴权,支持CAS单点登录 |
| 任务调度 | JUC + Quartz | 客户端用JUC线程池,服务端用Quartz做定时监测 |
| 持久层 | MyBatis-Plus + Druid | ORM + 数据库连接池 |
| 数据库 | MySQL 5.7+ | 约45张业务表,涵盖监控数据的实时态与历史态 |
| 前端 | Layui + ECharts + jtopo + xterm | 管理界面 + 图表展示 + 拓扑图 + Web终端 |
| 数据安全 | AES / DES / SM4 + Gzip | 传输数据先压缩再加密,支持国密算法 |
| 服务器采集 | oshi / Sigar | 跨平台硬件信息采集 |
| Docker采集 | docker-java | Docker API 交互 |
| 网络设备采集 | snmp4j | SNMP 协议通信 |
| 诊断工具 | Alibaba Arthas | 在线Java诊断 |
六、数据流转:一个心跳包的生命周期
以最基础的心跳机制为例,来看数据在 Phoenix 中是怎样流转的:
1. 客户端构造心跳包
客户端的 HeartbeatTaskScheduler 以固定频率(默认30秒)构造 HeartbeatPackage,包含应用实例ID、IP、端点类型等基本信息。
2. 数据加密与压缩
心跳包序列化为 JSON 后,经过 MsgPayloadUtils.encryptPayloadTo() 处理:
- 判断是否需要 Gzip 压缩(数据量较大时自动压缩)
- 使用配置的加密算法(AES/DES/SM4)对数据加密
- 封装为
CiphertextPackage(密文数据包)
3. 传输到服务端
根据通信协议配置,通过 HTTP POST 或 WebSocket 发送到 phoenix-server(也可通过 phoenix-agent 中转)。
4. 服务端解密与处理
服务端的 RequestPackageDecryptAdvice 拦截请求,自动完成解密和解压,还原出原始的 HeartbeatPackage。
5. 业务处理
HeartbeatServiceImpl.dealHeartbeatPackage() 将应用实例信息写入/更新到 MONITOR_INSTANCE 表。
6. 状态监测与告警
Quartz 定时任务 InstanceMonitorJob 周期性扫描实例表,发现心跳超时的实例后:
- 判断是否已开启告警
- 构造告警信息(包含应用ID、名称、IP、环境、分组等)
- 通过
TemplateMsgSendFacadeServiceImpl发送到对应的告警通道
这个流程体现了 Phoenix 的核心设计理念:采集 → 加密传输 → 持久化 → 异步监测 → 智能告警。
七、数据库设计概览
Phoenix 的数据库采用 MySQL,设计了约 45 张业务表,大致可以分为以下几个域:
| 数据域 | 核心表 | 说明 |
|---|---|---|
| 告警 | MONITOR_ALARM_DEFINITION、MONITOR_ALARM_RECORD、MONITOR_ALARM_RECORD_DETAIL |
告警定义、记录与发送详情 |
| 应用实例 | MONITOR_INSTANCE |
通过心跳上报的Java应用实例 |
| JVM | MONITOR_JVM_MEMORY、MONITOR_JVM_THREAD、MONITOR_JVM_RUNTIME、MONITOR_JVM_CLASS_LOADING、MONITOR_JVM_GARBAGE_COLLECTOR |
JVM各维度的实时数据 |
| 线程池 | MONITOR_JAVA_THREAD_POOL、MONITOR_JAVA_THREAD_POOL_HISTORY |
应用Java线程池的实时和历史数据 |
| 服务器 | MONITOR_SERVER、MONITOR_SERVER_CPU、MONITOR_SERVER_MEMORY、MONITOR_SERVER_DISK、MONITOR_SERVER_NETCARD、MONITOR_SERVER_OS 等 |
服务器各硬件维度数据 |
| Docker | MONITOR_DOCKER、MONITOR_DOCKER_CONTAINER、MONITOR_DOCKER_IMAGE、MONITOR_DOCKER_EVENT、MONITOR_DOCKER_STATS |
Docker全量信息 |
| 网络/TCP/HTTP | MONITOR_NET、MONITOR_TCP、MONITOR_HTTP 及对应的 History 表 |
连通性检测数据 |
| 网络设备 | MONITOR_NETWORK_DEVICE、MONITOR_NETWORK_DEVICE_SYS、MONITOR_NETWORK_DEVICE_IF |
SNMP设备信息 |
| 系统 | MONITOR_USER、MONITOR_ROLE、MONITOR_CONFIG、MONITOR_LOG_OPERATION、MONITOR_LOG_EXCEPTION |
用户、角色、配置、日志 |
一个重要的设计模式是实时表 + 历史表的分离:例如 MONITOR_SERVER_CPU 存储最新一次采集的 CPU 数据,而
MONITOR_SERVER_CPU_HISTORY 按时间序列存储所有历史数据。这样查询最新状态时效率很高,历史趋势分析也不受影响。
八、本系列博客规划
本系列将细分为几十篇,每篇聚焦一个具体主题,力求讲透讲深。以下是目前规划的脉络(后续可能随写作进展动态调整):
第一部分:通信与数据安全
| 篇目 | 主题 |
|---|---|
| 第二篇 | HTTP通信通道:请求/响应模型与RestTemplate封装 |
| 第三篇 | WebSocket通信通道:基于Netty的长连接实现 |
| 第四篇 | Netty WebSocket服务端启动与初始化流程 |
| 第五篇 | Netty WebSocket客户端连接与重连机制 |
| 第六篇 | 数据加解密体系:AES、DES、SM4国密算法的统一抽象 |
| 第七篇 | 数据压缩与传输优化:Gzip策略与CiphertextPackage设计 |
第二部分:客户端SDK
| 篇目 | 主题 |
|---|---|
| 第八篇 | Monitor入口类:客户端启动全流程解析 |
| 第九篇 | 配置加载机制:properties文件与SpringBoot配置的双轨制 |
| 第十篇 | 心跳机制:HeartbeatTaskScheduler的设计与实现 |
| 第十一篇 | JVM信息采集:内存、线程、GC、类加载数据的收集 |
| 第十二篇 | 线程池信息采集:MonitoredExecutor注册表与定时上报机制 |
| 第十三篇 | SpringBoot Starter自动配置:@EnableMonitoring注解原理 |
| 第十四篇 | SpringMVC Integrator适配:Listener机制集成方案 |
| 第十五篇 | 业务埋点:同步与异步告警发送API设计 |
第三部分:代理端
| 篇目 | 主题 |
|---|---|
| 第十六篇 | 代理端整体架构与角色定位 |
| 第十七篇 | 服务器信息采集(上):oshi方案详解 |
| 第十八篇 | 服务器信息采集(下):Sigar方案与oshi对比 |
| 第十九篇 | Docker信息采集:docker-java的封装与使用 |
| 第二十篇 | Docker容器资源统计:Stats采集与IO速率计算 |
| 第二十一篇 | 网络设备采集:snmp4j与SNMP协议交互 |
| 第二十二篇 | 代理端的数据转发与命令执行器模式 |
第四部分:服务端核心
| 篇目 | 主题 |
|---|---|
| 第二十三篇 | 服务端数据接收架构:Controller层设计 |
| 第二十四篇 | 并行数据处理:CompletableFuture异步编排实践 |
| 第二十五篇 | 应用实例管理:心跳处理与在线状态维护 |
| 第二十六篇 | 服务器监控数据处理:多维信息的入库与更新策略 |
| 第二十七篇 | Docker监控数据处理:系统/容器/镜像/事件/统计的全链路 |
| 第二十八篇 | 数据库监控:MySQL/Oracle/Redis/Mongo的连接与信息采集 |
| 第二十九篇 | 慢SQL检测:MySQL与Oracle的慢查询发现机制 |
| 第三十篇 | 网络/TCP/HTTP状态监测:Ping、端口探测与接口可用性检查 |
| 第三十一篇 | 分布式锁设计:基于数据库的轻量级分布式锁实现 |
| 第三十二篇 | 实时监控表与告警收敛:MONITOR_REALTIME_MONITORING的设计 |
第五部分:告警引擎
| 篇目 | 主题 |
|---|---|
| 第三十三篇 | 告警定义体系:类型、级别与编码设计 |
| 第三十四篇 | 告警触发流程:从状态变化到告警包构造 |
| 第三十五篇 | 告警通道实现(一):电子邮件 |
| 第三十六篇 | 告警通道实现(二):钉钉 |
| 第三十七篇 | 告警通道实现(三):企业微信 |
| 第三十八篇 | 告警通道实现(三):飞书 |
| 第三十九篇 | 告警收敛策略:频率控制与风暴抑制 |
第六部分:UI端与安全
| 篇目 | 主题 |
|---|---|
| 第四十篇 | UI端整体架构:SpringBoot + Thymeleaf + Layui |
| 第四十一篇 | SpringSecurity认证体系:自有认证流程详解 |
| 第四十二篇 | CAS单点登录集成:第三方认证方案 |
| 第四十三篇 | 验证码机制与登录安全防护 |
| 第四十四篇 | 操作日志与异常日志:AOP切面的日志记录设计 |
| 第四十五篇 | 前端交互设计:Layui模块化与ECharts图表实践 |
| 第四十六篇 | 拓扑图实现:jtopo的链路可视化 |
第七部分:线程池监控
| 篇目 | 主题 |
|---|---|
| 第四十七篇 | 线程池注册体系:MonitoredExecutor构造器注册与@MonitoringThreadPool注解辅助纳管 |
| 第四十八篇 | 线程池运行时动态调参:核心参数的热更新实现 |
| 第四十九篇 | 线程池历史趋势:数据记录与利用率分析 |
第八部分:公共模块与工程化
| 篇目 | 主题 |
|---|---|
| 第五十篇 | phoenix-common-core:DTO体系与AbstractSuperBean设计 |
| 第五十一篇 | phoenix-common-core:Domain模型与Server信息建模 |
| 第五十二篇 | phoenix-common-web:HTTP加解密切面的AOP实现 |
| 第五十三篇 | Maven多模块工程管理:依赖版本统一与构建配置 |
第九部分:部署与运维
| 篇目 | 主题 |
|---|---|
| 第五十四篇 | Docker部署:Dockerfile编写与镜像构建 |
| 第五十五篇 | Docker Compose一键部署方案 |
| 第五十六篇 | Windows/Linux服务化部署与启停脚本 |
| 第五十七篇 | 集群部署架构与高可用方案 |
第十部分:数据库设计
| 篇目 | 主题 |
|---|---|
| 第五十八篇 | 数据库整体设计:表分域、索引策略与外键约束 |
| 第五十九篇 | 实时表+历史表双表模式的设计思想 |
| 第六十篇 | Quartz调度表与定时任务持久化 |
九、小结
Phoenix 作为一个完整的开源监控平台,麻雀虽小,五脏俱全。它的设计有几点值得学习:
- 分层清晰:四端分离,公共模块下沉,客户端轻量易集成
- 通信安全:传输数据全程加密,支持多种加密算法,包括国密SM4
- 可扩展:基于 HTTP + WebSocket 的协议设计,天然支持跨语言接入
- 实用导向:不追求大而全,聚焦实际运维场景中最常见的监控需求
- 数据设计合理:实时+历史双表、完善的索引设计、外键约束保证数据一致性
从下一篇开始,我们将深入源码,逐层剥开 Phoenix 的技术内核。
评论