> ## Documentation Index
> Fetch the complete documentation index at: https://docs.akria.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Day 29 | 综合大项目需求分析

> 毕业设计：构建混合云资源监控平台架构设计与技术拆解

<Info>
  **今日目标**：Week 5 是整个 30 天计划的终局之战。我们将模拟一个真实的互联网大厂需求，从 0 开始设计并实现一个 **Master-Agent 架构** 的监控系统。今天不写代码，而是要像架构师一样思考。我们要彻底理解 **"为什么要用 Master-Agent 架构"**、**"Push vs Pull 模型区别"** 以及 **"如何设计高性能的时序数据表"**。
</Info>

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="book" size={26} color="#059669" /> 学习内容 (30 mins)</span>

在开始写代码前，先搞懂这些核心概念，否则你的系统一上线就会崩。

<AccordionGroup>
  <Accordion title="1. 架构模式：Master-Agent (10 mins)" icon="sitemap">
    **为什么不直接 SSH 上去查？**

    * **SSH 方案**: 慢，难以并行，且保存 SSH Key 有极大的安全隐患。
    * **Master-Agent 方案**:
      * **Master (大脑)**: 集中管理数据，提供 API。
      * **Agent (触手)**: 部署在每台机器上，只负责采集和上报。
    * **优势**:
      1. **解耦**: 业务机器不需要知道 Master 的逻辑，由于 Agent 极轻量，即便挂了也不影响业务。
      2. **安全**: Agent 不需要开放端口（只出不进），利用 Outbound HTTP 连接，无需在防火墙打洞。
  </Accordion>

  <Accordion title="2. 数据模型：Push vs Pull (10 mins)" icon="arrow-right-arrow-left">
    **谁主动？**

    * **Pull 模型 (Prometheus)**: Master 主动去连 Agent 抓数据。
      * 优点：Master 控制全局，知道谁挂了。
      * 缺点：Agent 必须有公网 IP 或在同一内网，且需要开放端口。
    * **Push 模型 (Our Project)**: Agent 主动把数据发给 Master。
      * 优点：**内网穿透**。Agent 在深层子网也能报数据，只要能访问公网 Master 即可。
      * 缺点：Master 不知道 Agent 是死是活（需要心跳机制补充）。
    * **结论**: 对于混合云环境（阿里云+腾讯云+办公室物理机），**Push 模型**更合适。
  </Accordion>

  <Accordion title="3. 数据库设计：时序数据 (10 mins)" icon="database">
    **监控数据的特点：**

    * **写多读少**: 每秒都有成千上万个点写入。
    * **时间敏感**: 我们只关心“最近 1 小时”或“最近 24 小时”。
    * **索引策略**: 必须在 `created_at` 上建索引，否则查询 Top N 时会全表扫描。
  </Accordion>
</AccordionGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="code" size={26} color="#059669" /> 设计任务 (90 mins)</span>

<Steps>
  <Step title="任务 A: 数据库建模 (MySQL)">
    我们需要一张能够支撑快速写入和按时间排序的表结构。

    <CodeGroup>
      ```sql 29_schema.sql theme={null}
      -- 1. 创建专用数据库
      -- 使用 utf8mb4 字符集以支持更多字符（虽然监控数据主要是数字）
      CREATE DATABASE IF NOT EXISTS monitor_system CHARACTER SET utf8mb4;

      USE monitor_system;

      -- 2. 创建指标表
      -- 这是一个典型的“时序数据”表 design
      CREATE TABLE IF NOT EXISTS server_metrics (
          -- 主键：自增 ID，大数据量下建议使用 BIGINT
          id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
          
          -- 维度字段：用于定位是谁报的数据
          hostname VARCHAR(64) NOT NULL COMMENT '主机名',
          ip_address VARCHAR(15) NOT NULL COMMENT '内网IP',
          
          -- 指标字段：核心监控数值
          -- 使用 FLOAT 存储百分比 (0.0 - 100.0)
          -- 为什么不用 DECIMAL？监控数据允许轻微精度损失，FLOAT 更省空间
          cpu_usage FLOAT NOT NULL COMMENT 'CPU使用率%',
          mem_usage FLOAT NOT NULL COMMENT '内存使用率%',
          disk_usage FLOAT NOT NULL COMMENT '磁盘使用率%',
          
          -- 时间字段：数据的生命线
          -- DEFAULT CURRENT_TIMESTAMP 让数据库自动打标
          reported_at DATETIME DEFAULT CURRENT_TIMESTAMP,
          
          -- 复合索引：加速查询
          -- 场景：查询 hostname='web-01' 在 '2023-10-01' 之后的数据
          INDEX idx_host_time (hostname, reported_at)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务器监控指标流水表';
      ```
    </CodeGroup>

    **代码解释**：

    * **`BIGINT UNSIGNED`**：大数据量下使用 BIGINT，避免 INT 溢出
    * **`FLOAT` vs `DECIMAL`**：监控数据允许精度损失，FLOAT 更省空间
    * **`INDEX idx_host_time`**：复合索引，加速按主机和时间查询
    * **`DEFAULT CURRENT_TIMESTAMP`**：数据库自动打时间戳

    **时序数据库的设计哲学**：

    * **写多读少**：优化写入性能，索引只建在查询字段上
    * **时间分区**：大数据量下按月分表，或定期清理旧数据
    * **维度+指标**：hostname 是维度，cpu/mem/disk 是指标

    **设计思考**：

    * *为什么不把 hostname 设为主键？* -> 因为每个主机每分钟都会上报多次，hostname 是重复的。它是"维度"，不是"主键"。
    * *数据会爆炸吗？* -> 会。100 台机器，每 5 秒一条，一天就是 `100 * 12 * 60 * 24 = 1,728,000` 条。生产环境通常会按月分表，或使用定期清理脚本。

    **验证步骤**:

    1. 进入 MySQL 容器：`docker exec -it mysql-learn mysql -uroot -proot`
    2. 粘贴 SQL 执行。
    3. `DESCRIBE monitor_system.server_metrics;` 确认结构无误。
  </Step>

  <Step title="任务 B: API 契约定义 (JSON)">
    Master 和 Agent 必须由统一的语言。即使还没写代码，先把 JSON 格式定死，前后端（以及 Go/Python）就能并行开发了。

    <CodeGroup>
      ```json payload_example.json theme={null}
      // POST /api/v1/report 的请求体 (Request Body)
      {
        "hostname": "prod-api-01",
        "ip": "10.0.1.5",
        "cpu": 45.2,   // 约定：必须是 0-100 之间的浮点数
        "mem": 60.8,
        "disk": 80.1
      }
      ```

      ```json response_example.json theme={null}
      // 成功响应 (Response Body)
      {
        "code": 200,
        "msg": "success"  // 简单明了，无需返回冗余数据
      }
      ```
    </CodeGroup>

    **常见错误**：

    <Warning>
      * ❌ **字段类型歧义**: Agent 传了字符串 `"45.2%"`，而 Master 期待的是数字 `45.2`。这会导致解析失败。
      * ❌ **空指针风险**: 如果 `disk` 采集失败了，是传 `0` 还是 `null`？这里我们约定：必须传数字，如果采集失败，Agent 需自行处理（如填 -1 或 上次的值）。
    </Warning>
  </Step>

  <Step title="任务 C: 工程目录规划">
    一个好的目录结构是成功的开始。我们将创建标准的“多语言混合项目”结构。

    <CodeGroup>
      ```bash 目录结构 theme={null}
      ~/final-project/
      ├── README.md           # 项目总说明
      ├── docker-compose.yaml # 整体编排
      ├── master/             # Python 控制面
      │   ├── main.py
      │   ├── models.py
      │   ├── schemas.py
      │   ├── requirements.txt
      │   └── Dockerfile
      ├── agent/              # Go 数据面
      │   ├── main.go
      │   ├── go.mod
      │   └── go.sum
      └── scripts/            # 辅助脚本
          └── init_db.sql
      ```
    </CodeGroup>

    **验证步骤**:

    1. 执行 `mkdir -p ~/final-project/{master,agent,scripts}`
    2. `touch ~/final-project/docker-compose.yaml`
    3. 把刚才的 SQL 保存为 `scripts/init_db.sql`。
  </Step>
</Steps>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="lightbulb" size={26} color="#059669" /> 拓展任务 (30 mins)</span>

<CardGroup cols={2}>
  <Card title="鉴权挑战" icon="lock">
    **思考**：现在 API 是公网裸奔的。任何知道 URL 的人都可以伪造数据 POST 给你。

    **任务**：设计一个简单的 `Authorization` 机制。

    * 方案：Master 在环境变量设置 `AGENT_SECRET=xyz`。Agent 发送请求时在 Header 带上 `X-Token: xyz`。Master 校验不通过则返回 403。
  </Card>

  <Card title="高可用思考" icon="server">
    **思考**：如果 Master 挂了，Agent 应该怎么办？

    **任务**：设计 Agent 的重试策略。

    * 方案：Agent 应该有一个内存 Buffer（比如存最近 10 条）。如果发送失败，先存 Buffer，等 Master 恢复了一次性发过去。
  </Card>
</CardGroup>

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="file" size={26} color="#059669" /> 今日产出物</span>

* `scripts/init_db.sql` - 数据库初始化脚本
* `README.md` (Draft) - 包含 API 格式说明
* 完整的项目骨架目录

***

## <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px' }}><Icon icon="github" size={26} color="#059669" /> 参考资源</span>

<CardGroup cols={2}>
  <Card title="OpenAPI 规范" icon="book" href="https://spec.openapis.org/oas/latest.html">
    了解为什么我们要先定义 JSON 格式
  </Card>

  <Card title="Google SRE Book" icon="google" href="https://sre.google/sre-book/monitoring-distributed-systems/">
    Google 对分布式监控系统的经典论述
  </Card>
</CardGroup>

***

## 实际应用场景

<CardGroup cols={2}>
  <Card title="Zabbix vs Prometheus" icon="chart-line">
    * **Zabbix**: 传统老牌，擅长 Push 模式，适合物理机、网络设备监控。我们做的这个项目更像 Zabbix 的简化版。
    * **Prometheus**: 云原生标配，擅长 Pull 模式，适合 K8s 动态环境。
  </Card>
</CardGroup>

<Tip>
  **Next Step**: 设计图纸已画好。明天（Day 30 Pt.1）我们将戴上安全帽，先把 **Master (大脑)** 搭建起来。请确保你的 Python 环境就绪。
</Tip>

***

<CardGroup cols={2}>
  <Card title="回到目录" href="/training/30daystudy/week-5">
    查看完整进度
  </Card>

  <Card title="下一阶段: Master 开发" href="/training/30daystudy/day-30-part1">
    Day 30 Pt.1 | Master 服务开发
  </Card>
</CardGroup>
