jubenavides/service-dependency-analyzer

GitHub: jubenavides/service-dependency-analyzer

一个从零手写核心数据结构与图算法的微服务依赖分析引擎,通过实时摄取服务依赖事件流,提供爆炸半径、关键路径和异常检测等查询能力。

Stars: 0 | Forks: 0

# 服务依赖分析器 一个事件响应引擎,它接收服务依赖事件流,维护一个(虚构的)生产环境的内存有向图,并回答关于它的分析问题 —— 爆炸半径、谁依赖于此、最低延迟路径、关键服务、循环和健康状态。 消息队列、有向图和每个图算法都是基于标准数据结构从头构建的 —— 没有 Kafka/Rabbit/NATS/Redis/SQS,没有 Neo4j/NetworkX,也没有图算法库。 Spring Boot 仅用于提供 HTTP API 并公开指标;除此之外无其他用途。 - 非技术概述:[`docs/product-spec.md`](docs/product-spec.md) - 工程设计与图表:[`docs/tech-spec.md`](docs/tech-spec.md) - 设计报告:[`docs/report.md`](docs/report.md) ## 环境要求 - **Java 25**(构建目标为 Java 25 工具链)。Spring Boot 3.5.x 已准备好支持 Java 25;如果你更喜欢其一流的 Java 25 支持,可以在 `build.gradle` 中升级到 Spring Boot 4.0.x。 - **Gradle 9.1+** —— Java 25 需要 Gradle 9.0+ 才能运行,而 9.1.0 提供了完整的 Java 25 支持。旧版 Gradle (8.x) 无法在 Java 25 上运行。或者直接使用 Docker(见下文),它会自动获取合适的 Gradle。 ## 快速开始(一条命令) ``` docker compose up --build ``` 这将构建 fat jar,在 `http://localhost:8080` 上启动服务,通过摄入管道重放已提交的样本数据集(`data/sample-events.ndjson`,约 5k 个服务 / 70k 个事件),并将状态持久化到 `./state` 目录下。 ## 本地运行 ``` gradle bootRun # 或者 gradle clean bootJar && java -jar build/libs/service-dependency-analyzer-0.1.0.jar ``` ## 生成全新数据集 ``` gradle generateDataset \ -Dsda.gen.services=6000 -Dsda.gen.events=120000 -Dsda.gen.out=data/events.ndjson # 然后将 sda.ingest.source 指向 data/events.ndjson (application.yml 或 SDA_INGEST_SOURCE) ``` ## 运行测试 完整的 JUnit 测试套件(跨 12 个文件的 48 个测试): ``` gradle test # HTML 报告: build/reports/tests/test/index.html # 单个 test class gradle test --tests 'com.defendermate.sda.persist.PersistenceTest' ``` `verify/` 目录中包含独立的验证预言机(Python;项目本身未使用任何图库 —— 这里的 `networkx` 仅用作测试参考): ``` pip install networkx python3 verify/algo_ref.py # Brandes/Tarjan/Dijkstra/BFS cross-checked vs networkx python3 verify/convergence.py # order-independent LWW + tombstone convergence python3 verify/ringmodel.py # ring-buffer FIFO/capacity model python3 verify/stretch_check.py # anomaly detection + time-travel logic python3 verify/gen_and_verify.py # generate a dataset + run a reference engine end-to-end ``` ## 使用 API 发布事件(单个对象或数组): ``` curl -s -X POST localhost:8080/events -H 'Content-Type: application/json' -d '{ "event_id":"e-1","type":"dependency_observed","timestamp":"2026-05-06T14:21:09.412Z", "source":"checkout-api","target":"payments-service","latency_ms":42,"status":"ok"}' # -> 202 {"accepted":1} ``` 查询图: ``` # 爆炸半径:service 下游的所有内容,及其路径 curl -s 'localhost:8080/graph/reachable?service=checkout-api' # 如果此服务宕机,谁会受到影响(反向可达性) curl -s 'localhost:8080/graph/dependents?service=postgres-primary' # 最低延迟路径(权重 = 每条边最近的滚动平均延迟) curl -s 'localhost:8080/graph/shortest-path?source=checkout-api&target=postgres-primary' # 最关键的 Top-k 服务(介数中心性) curl -s 'localhost:8080/graph/critical?k=10' # 当前存在的所有依赖循环 curl -s 'localhost:8080/graph/cycles' # 尾部窗口(秒)内的错误率 + p95 延迟 curl -s 'localhost:8080/graph/health?service=auth&window=300' # 异常检测:最近的延迟/错误率偏离其基线的边 curl -s 'localhost:8080/graph/anomalies?recent=120&baseline=600&latency-factor=1.5&error-delta=0.2' ``` 时间漫游 —— 针对过去某一时刻的图状态回答相同的问题(`at` 接受 ISO-8601 格式或 Unix 纪元毫秒): ``` curl -s 'localhost:8080/graph/at/stats?at=2026-05-06T14:00:00Z' curl -s 'localhost:8080/graph/at/reachable?service=checkout-api&at=1746540000000' curl -s 'localhost:8080/graph/at/cycles?at=2026-05-06T14:00:00Z' ``` 错误均为结构化的,绝不会出现堆栈跟踪: ``` { "error": "unknown_service", "message": "unknown service: foo", "status": 404 } ``` 可观测性: ``` curl -s localhost:8080/actuator/health curl -s localhost:8080/actuator/metrics/sda.query.latency curl -s localhost:8080/actuator/prometheus | grep sda_ ``` 结构化(ECS JSON)日志将输出到 stdout。 ## 手动冒烟测试(Docker) 你可以手动运行的完整端到端演示。 **1. 启动它**(前台运行;添加 `-d` 以在后台运行): ``` docker compose up --build ``` **2. 等待直到健康**(在第二个终端中)。启动时,它会通过摄入管道重放 `data/sample-events.ndjson`(约 5k 个服务 / 70k 个事件)。 ``` curl -s localhost:8080/actuator/health # {"status":"UP"} docker compose ps # STATUS shows "healthy" ``` **3. 查询已加载的图。** 该样本使用了 `svc-00000…` 节点,以及高扇入(high-fan-in)的中心节点 `auth`、`postgres-primary`、`redis-cache` 和 `config-service`: ``` curl -s 'localhost:8080/graph/dependents?service=postgres-primary' curl -s 'localhost:8080/graph/reachable?service=svc-00000' curl -s 'localhost:8080/graph/critical?k=10' curl -s 'localhost:8080/graph/cycles' curl -s 'localhost:8080/graph/health?service=auth&window=600' curl -s 'localhost:8080/graph/anomalies' ``` **4. 发布一个新事件并立即查看其反映的结果:** ``` curl -s -X POST localhost:8080/events -H 'Content-Type: application/json' \ -d '{"event_id":"smoke-1","type":"dependency_observed","timestamp":"2030-01-01T00:00:00Z", "source":"my-api","target":"postgres-primary","latency_ms":5,"status":"ok"}' curl -s 'localhost:8080/graph/reachable?service=my-api' # -> postgres-primary ``` **5. 时间漫游**(未来某一时刻的完整图;越早的时刻显示的图越小): ``` curl -s 'localhost:8080/graph/at/stats?at=2030-01-01T00:00:00Z' ``` **6. 结构化错误**(无堆栈跟踪): ``` curl -si 'localhost:8080/graph/reachable?service=does-not-exist' # 404 unknown_service ``` **7. 观察它** —— 指标和结构化日志: ``` curl -s localhost:8080/actuator/metrics/sda.queue.depth curl -s localhost:8080/actuator/prometheus | grep sda_ docker compose logs -f sda # JSON logs ``` **8. 优雅关闭**(SIGTERM → 排空处理中的事件 + 最终检查点),然后重启并确认图从 `./state` 下的持久化状态恢复: ``` docker compose stop # drains + snapshots; state kept on the ./state volume docker compose up -d # restarts; recovers snapshot + WAL, answers identically docker compose down # remove containers (./state remains on disk) ``` ## 配置项 所有设置都位于 `src/main/resources/application.yml` 的 `sda.*` 下,并可通过环境变量覆盖(松散绑定:`sda.pipeline.producers` → `SDA_PIPELINE_PRODUCERS`)。 | 键 | 默认值 | 含义 | |---|---|---| | `sda.queue.capacity` | 50000 | 有界环形缓冲区容量 | | `sda.queue.shed-load` | false | `false` = 生产者阻塞(背压);`true` = 丢弃并计数 | | `sda.pipeline.producers` | 4 | 并发文件重放生产者(≥ 2) | | `sda.pipeline.consumers` | 4 | 并发消费者(≥ 2) | | `sda.ingest.source` | data/sample-events.ndjson | 启动时重放的文件;如果缺失 → 仅限 API 模式 | | `sda.persistence.dir` | state | 用于快照 + WAL 的目录 | | `sda.persistence.snapshot-every-events` | 20000 | 触发计划检查点前应用的最少事件数 | | `sda.persistence.snapshot-every-seconds` | 30 | 检查点调度器间隔 | | `sda.health.default-window-seconds` | 300 | `health()` 的默认滑动窗口 | | `sda.query.betweenness-sample` | 0 | 0 = 精确 Brandes 算法;> 0 = 抽样 N 个源点(近似值) | ## 测试与验证 JUnit 套件(使用 `gradle test` 运行): - **Queue** —— FIFO、容量/offer、阻塞 put/take、并发下的无丢失/无重复。 - **Graph** —— 后写覆盖的存在性、乱序容忍、墓碑机制、确定性的元数据决胜、双重邻接表、滑动平均权重。 - **Ingestion** —— 幂等性、乱序容忍、格式错误行处理、无损排空。 - **Persistence** —— 重启一致性(快照 + WAL 重放可重现完全一致的结果)。 - **Algorithms** —— 在手工制作的图上测试 reachable/dependents/shortest-path/critical/cycles/health。 - **Time-travel** —— 历史重建准确反映 `ts ≤ T` 的事件。 - **Anomaly detection** —— 降级的边会对照其基线被标记;稳定/安静的边则不会。 - **API** —— 路由、参数、JSON 结构、结构化错误(查询、时间漫游、摄入)。 [`verify/`](verify/) 目录包含开发过程中使用的独立于语言的预言机: 核心算法的 Python 重新实现及收敛语义,在数百个随机图上与 `networkx` 进行交叉验证,外加一次端到端的生成器+引擎健全性测试运行。相关命令请参阅上方的[运行测试](#run-the-tests)。 ## 假设与限制 - **Timestamps**(时间戳)驱动所有冲突解决机制。节点的存在性遵循后写覆盖原则;当且仅当一条边最新观测的时间严格晚于其最新移除的时间时,该边才是活跃的。这使得图成为事件集合的函数,与到达顺序无关 —— 这也是重启一致性的基础。 - **Cycles**(循环)以强连通分量(大小 > 1)及自环的形式报告。枚举所有简单循环的复杂度是指数级的(Johnson 算法);循环组是具有可操作性且有限范围内的答案。 - **Criticality**(关键性)采用介数中心性(详见报告)。精确计算的复杂度为 O(V·E);在非常大的图上,可设置 `sda.query.betweenness-sample > 0` 对源点进行抽样。 - 每条边的 **Rolling windows**(滑动窗口)都是有界的(≤ 512 个样本 / 15 分钟),因此长于保留期限的 `health` 窗口会受到已保留内容的限制。 - **Durability**(持久性)采用快照 + WAL 并结合刷写频率;崩溃最多可能丢失最后几个未刷写的事件,绝不会丢失已刷写的事件,也不会破坏状态。 - **Time-travel**(时间漫游)由一个只追加的事件归档(`state/history.ndjson`)提供支持,该归档从不会被截断;重建过程会重放 `ts ≤ T` 的前缀部分。无限保留历史是一种经过深思熟虑的权衡(在生产环境中可采取分段/过期策略)。 - **Anomaly detection**(异常检测)将每条边的近期窗口与其自身的基线窗口进行对比,因此无需全局阈值;这两个窗口都必须达到 `min-samples` 的最低要求,以避免安静边产生的噪声。 ## 项目结构 ``` src/main/java/com/defendermate/sda/ queue/ BoundedBlockingQueue — hand-built ring buffer graph/ Graph, Edge, ServiceNode, RollingWindow, snapshot types model/ Event, EventType, Status ingest/ EventCodec, EventApplier, IngestionPipeline, sources persist/ WriteAheadLog, FileWriteAheadLog, SnapshotStore, PersistenceManager query/ GraphAlgorithms, QueryService, result records api/ controllers, error handling config/ properties, Spring wiring, startup/shutdown lifecycle gen/ DatasetGenerator ```
标签:Spring Boot, 事件驱动, 后台面板检测, 图分析, 域名枚举, 库, 应急响应, 服务依赖分析, 请求拦截, 运维