# APM **Repository Path**: xyx199395/apm ## Basic Information - **Project Name**: APM - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-15 - **Last Updated**: 2026-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # APM — 应用健康监测系统(镜像流量旁路分析) 基于 FBP(流式)管道的旁路应用性能监测:交换机端口镜像(SPAN)流量进, 应用级调用/会话明细与健康指标出。面向 **5 Gbps** 级镜像流量设计。 ``` 交换机镜像口 │ eBPF/XDP 端口白名单(可选):非业务流量在网卡入口即丢弃 │ AF_PACKET + PACKET_FANOUT(N 个 socket 按流哈希分流) ▼ capture ──► decode ──► demux ──► session×N ──┬──► agg ──► Prometheus 导出(:9400) (帧) (L2/L3/L4) (按流哈希) (TCP重组 + │ ▲ │ │ L7解析) └──► ClickHouse(调用/会话明细) │ └──(通道满时)──► spill 磁盘段文件 ──(消退后最旧优先重放)──┘ ``` - **管道引擎**:`pkg/flow` — go-flow 风格的类型化 FBP 节点/端口(节点=协程,端口=带缓冲通道,背压/丢弃计数),原 vmware/go-flow 已下线,故内置实现。 - **采集**:Linux `af_packet` 多队列扇出(`FanoutHash`),零 cgo,纯 Go 交叉编译;开发/回放用 `pcap` 文件模式(Windows 可跑)。 - **XDP 前置过滤**:`bpf/xdp-filter.c` + `internal/xdpfilter`(cilium/ebpf 加载,generic/driver 两种挂载模式)— 端口级白名单 ARRAY map,在网卡入口丢弃非业务流量并在 eBPF map 侧计数(`apm_xdp_drops_total`);解析不了的帧一律放行(fail-open)。附 `cmd/xdptest` 手动验证工具与 `deploy/verify-xdp-*.sh` 差分验证脚本。 - **磁盘背压(spill)**:`internal/spill` — 采集侧 `TrySend` 失败即把帧批写入滚动段文件(硬容量上限,超限丢最旧),拥塞消退后最旧优先重放回管道,原始时间戳保留;写入静默约 1s 后当前段自动转段重放,不留尾巴。 - **会话**:`internal/session` — 按五元组哈希分片、无锁分片表;TCP 重组(绕回安全的序号差、重传/乱序统计、零窗口跳变、SYN→SYN/ACK 建连时延)、FIN/RST 状态机、空闲老化。 - **L7 解析**:`internal/proto` — HTTP/1.x(Content-Length + chunked,请求/响应配对比对耗时)、**TLS 握手观测**(ClientHello SNI + 协商版本、Alert 失败,不解密)、MySQL(COM_QUERY / OK / ERR / 结果集帧相位)、Redis(RESP,命令 + 错误)、Oracle TNS(DATA 报文中明文 SQL 语句提取 + `ORA-NNNNN` 错误码;压缩/加密链路自动退化为报文类型级指标)。 - **应用映射**:`internal/appmap` — IP CIDR → 应用名(最长前缀优先);条目可加可选 `ports` 限定服务端端口,同网段不同端口的业务(如 docker 面板)可独立成应用,带端口规则优先于纯网段规则。 - **健康评分**:`internal/agg` — 每窗口 `100 − 错误率惩罚(≤50) − p99 延迟惩罚(≤30) − 重传惩罚(≤20)`。 - **存储/展示**:ClickHouse 原生协议批量落库(`apm.calls` / `apm.conns`,见 `sql/schema.sql`),Grafana 直连;Prometheus 文本导出器看实时分数。 ## 目录结构 | 路径 | 说明 | |---|---| | `pkg/flow` | FBP 管道引擎(Graph / Port[T]) | | `internal/model` | Frame / PacketMeta / CallRecord / ConnRecord / AppMetrics | | `internal/capture` | afpacket(Linux)/ pcap(回放)采集源 | | `internal/xdpfilter` `bpf/xdp-filter.c` | XDP 加载/挂载(Go)+ 端口白名单过滤(BPF C) | | `internal/spill` | 磁盘背压段文件:落盘、滚动、最旧优先重放 | | `internal/decode` | gopacket 解码 → PacketMeta | | `internal/demux` | 按 FlowID 哈希分发到 N 个 worker | | `internal/session` | TCP 会话重组 + 状态机 + L7 挂接 | | `internal/proto` | HTTP / TLS(SNI) / MySQL / Redis / Oracle 解析器 | | `internal/appmap` `internal/agg` | IP→应用映射;窗口聚合与评分 | | `internal/sink` | ClickHouse 落库;Prometheus 导出 | | `internal/web` `web/` | 自托管 Web 控制台(嵌入式 SPA + JSON API + 配置中心) | | `internal/pipeline` `cmd/collector` | 图组装;主程序 | | `configs/collector.yaml` `sql/schema.sql` | 配置样例;ClickHouse DDL + Top-N 慢接口物化视图 | | `dashboards/apm-overview.json` | Grafana 总览仪表盘(健康评分 / p99 / 错误率 / TOP10 慢接口) | ## 快速开始 ```bash # 1. 离线回放(Windows/Linux 均可) go build -o bin/collector ./cmd/collector ./bin/collector -config configs/collector.pcap.yaml # capture.mode: pcap # 2. 生产(Linux, root 或 CAP_NET_RAW) sudo ./collector -config configs/collector.yaml # capture.mode: afpacket # 3. 指标 curl -s localhost:9400/metrics | grep apm_app_score # 4. 明细入库(可选) clickhouse-client --multi < sql/schema.sql # 再置 sink.clickhouse.enabled: true # 5. 可视化 # Grafana 导入 dashboards/apm-overview.json(选择 Prometheus 与 ClickHouse 数据源) # 健康评分/p99/错误率/QPS/抓包健康 来自 Prometheus;TOP10 慢接口 来自 ClickHouse 物化视图 ``` 交叉编译生产二进制: ```bash GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o collector ./cmd/collector ``` ## 部署要点(5 Gbps) 1. 镜像口所在网卡:`ethtool -L eth0 combined N`、开 GRO/RSS、Ring Buffer 调大;collector 进程绑核(`taskset`)。 2. `capture.sockets` ≈ 分配核数;同机多进程用相同 `fanout_id`(内核按流哈希保证同流同 socket)。 3. 观察 `apm_port_messages_total{state="dropped"}` 与 `apm_capture_drops_total`:持续增长则加 socket/worker 或收窄 BPF/镜像范围。 4. 会话表上限 `flow.max_sessions`(默认 200 万,约数 GB RSS),超出按最久未活跃近似 LRU 淘汰。 5. **XDP**:先开 `generic_mode: true`(兼容虚拟网卡),物理网卡确认驱动支持后改 driver 模式。端口白名单务必包含运维端口(22 由程序强制追加);上线前用 `cmd/xdptest` + `tcpdump` 差分验证(被过滤端口应"零包")。 6. **spill**:启用后观察 `apm_spill_frames_spilled_total` 与 `apm_spill_frames_replayed_total` 是否收敛;`spilled` 持续增长而 `replayed` 追不上说明持续过载,应扩容 worker 或前置 XDP 收窄流量。 ## HTTP / HTTPS 说明 旁路镜像不解密:HTTP 明文得到 URL 级指标;HTTPS 通过 **ClientHello SNI** 得到域名级调用指标 (握手时延、成功率、协商版本),并按 `protocols.tls.sni_map`(域名后缀最长匹配)归并到应用, 未命中则回落 server IP→appmap。SNI 仅在会话首个 ClientHello 出现且明文可读时可得 (ESNI/ECH 或中间件已代理终结则不可得)。需要 URL 级 HTTPS 指标时,建议服务端侧接入 (如 nginx access log 补采),本系统负责全量连接质量(时延/重传/零窗口/建连失败)。 ## Web 控制台 collector 内置自托管 Web 控制台(Vue 3 + Element Plus SPA 编译产物经 `//go:embed` 打进二进制, 零外部依赖),八页:健康墙(评分卡 + QPS 迷你趋势)、**问题定界**(三段定界 TTFB/TXFER/GAP + 责任归因 + 故障窗口 pcap 下钻)、应用详情(扣分拆解 + 分数/QPS/延迟趋势)、 接口/SQL 排行(慢/错误率/调用量/流量四种排序,物化视图)、会话检索(conns 明细 + 连接四元组 下钻调用时间线,含三段拆分)、报文归档(pcap 分片检索/下载)、引擎健康(采集/XDP/spill/端口)、配置中心。 **配置中心**:`internal/web` 用反射枚举 `config.Config` 全部 YAML 叶子字段并生成 schema, 页面表单覆盖 100% 配置面;保存走 YAML Node 树手术(保留键序与未改动注释)→ 校验(空密码=保留 已存密码)→ 自动备份(`/config-backups/`,保留 30 份)→ 临时文件原子替换 → JSONL 审计。仅 `log.level` 与 `xdp.ports` 真正热生效(schema 里标"热更新"),其余字段保存后 一键重启生效(`POST /api/v1/restart` 退出进程,由 systemd `Restart=always` 拉起)。 **安全模型**:读接口开放(对齐 Prometheus 导出器姿态);全部写接口需要服务端环境变量 `APM_WEB_TOKEN`(系统启动时读取,控制台不落任何文件、不进 collector.yaml/审计), 请求头 `Authorization: Bearer ` 或 `X-APM-Token`(常数时间比较)。未设该变量时 控制台严格只读。监听默认 `127.0.0.1:9500`,经 SSH 隧道访问: `ssh -L 9500:127.0.0.1:9500 root@server`,浏览器打开 `http://127.0.0.1:9500`。 配置下载/回显均对 `sink.clickhouse.password` 脱敏。后端自身不发起任何 URL 请求 (明细查询走 ClickHouse 原生协议)。 ```yaml # collector.yaml web: enabled: true listen: 127.0.0.1:9500 # 建议保持回环;写操作需 APM_WEB_TOKEN 环境变量 ``` 前端开发/构建(产物 `internal/web/dist`,由 Go 二进制嵌入): ```bash cd web && npm install && npm run build # 产物即被 embed;之后 go build 即可 npm run dev # 开发模式,Vite 代理 /api → 127.0.0.1:9500 ``` ## 开发 ```bash go test ./... # 单元 + 端到端(pcap 回放路径,Windows 可跑) go vet ./... GOOS=linux go build ./... # afpacket 分支编译检查 ``` ## Roadmap ### 已完成(真实 Linux 服务器闭环验证) - **eBPF/XDP 前置过滤**:`bpf/xdp-filter.c` 端口白名单 + `internal/xdpfilter` 加载挂载(generic/driver),内核侧丢弃计数 `apm_xdp_drops_total`。 验证方法:回环差分 + VERDICT 断言(白名单端口通、非白名单端口断),网卡侧用 `tcpdump` 差分——XDP 在 AF_PACKET 抓包点之前丢帧, 被过滤端口在 tcpdump 里应是**零包**,放行端口完整可见,这是最硬的证据。 - **磁盘背压(spill)**:raw-SYN 风暴打满单 worker 通道实测——431 万帧落盘(`apm_spill_frames_spilled_total`),风暴停止后 ~10s 全部重放 (`spilled == replayed`、`apm_spill_bytes_pending=0`、spill 目录清空),原始时间戳保留。 - **Web 控制台**:内置 SPA + 配置中心全量上线,生产服务器(真实 ClickHouse 数据)闭环实测——六页面、 Top-N 四种排序/会话检索四元组下钻、配置保存→备份→审计→重启→生效全链路(含 log.level 热更新与 XDP 白名单热更新接口)。 - **N2 应用深度监测**:三段定界(TTFB/TXFER/GAP)+ `flow_id` + 慢样本留存(`apm.slow_samples`)+ `GET /api/v1/segments` 定界接口。真实流量实测:服务端 sleep 350ms 的应用量出 TTFB≈352ms、 判读为"服务端处理慢";`flow_id` 填充率 100%;自洽性 0 违规。详见 `docs/N2-应用深度监测-实施记录.md`。 - **N3 问题判断与定界**:控制台新增「问题定界」页 —— 一句话结论 + 故障窗口异常表(级别/三段/归因) + 该窗口 pcap 分片一键下载 + 判读规则表;`GET /api/v1/fault-window` 与 `GET /api/v1/calls/by-flow` 两个只读接口;判读**错误优先于延迟**。详见 `docs/N3-问题判断与定界-实施记录.md`。 ### 踩坑记录 - **XDP 动作码是 ABI**:`XDP_ABORTED=0 / XDP_DROP=1 / XDP_PASS=2`(uapi/linux/bpf.h)。初版 C 代码把 PASS/DROP 宏自行定义成 1/2, 得到一个"完美反装"的过滤器——map、relocation、加载链路全部正常,靠 tcpdump 差分才暴露。写 BPF C 请直接核对 uapi 值。 - **af_packet 环关闭必须单点**:另起 goroutine 在 ctx 取消时 `TPacket.Close()` "解封"阻塞的 poll = 与 `pollForFirstPacket` 抢 mmap 环解映射,SIGSEGV(正是生产 `systemctl stop` 必现 failed 的根因)。正确做法:pump 靠 poll 超时自行退出,`wg.Wait()` 后恰好一次 Close。 - **spill 尾段问题**:`Run` 只重放"已关闭"段,写入停止后残留的半截 active 段永远无人认领(实测 pending 冻结在一段大小)。 修复:写入静默约 1s 后自动转段交给重放(`rotateIfIdle`),保证收敛闭环。 - **环境经验**:`bpftool` 装不上时,tcpdump 差分就是地面真值;本机 curl 自己的公网 IP 走 lo、不经过 eth0,XDP 的网卡级验证必须从真正的外部发起。 - **ClickHouse 别名遮蔽(第二处)**:物化视图查询里 `countIfMerge(errs) AS errs` 的别名会遮蔽 `errs` 原始列, 同 SELECT 中稍后的 `countIfMerge(errs)` 会拿到已合并的 UInt64 而报 `ILLEGAL_TYPE_OF_ARGUMENT`。 正确顺序:先 `countMerge(calls) AS calls, countIfMerge(errs) AS errs`,再 `errs / calls AS err_rate`。 - **clickhouse-go 严格扫描**:驱动拒绝跨宽度 Scan(如 Float32→*float64、UInt8→*uint64)。 聚合结果宽度不由你定(quantile 对 UInt32 返回 Float32、字面量 `0` 推断为 UInt8), SQL 里一律显式 `toFloat64(...)` / `toUInt64(...)` 再扫。 - **时区口径**:collector 以服务器本地时区(UTC+8)写 `ts`,ClickHouse 侧按 UTC 解读,明细时间显示早 8 小时 (窗口查询因偏移恒定仍正确)。接入展示前应统一:建议 sink 写 UTC 或 CH session 时区对齐。 ### 待办 - Oracle 深度解析为启发式(明文 DATA 提取);如链路启用 Transparent Data Encryption/压缩,需专用解码 - HTTPS URL 级指标(服务端日志补采旁路)