226 lines
12 KiB
Markdown
226 lines
12 KiB
Markdown
# 蔬菜价格指数平台 — 架构设计
|
||
|
||
> 版本:1.2
|
||
> 范围:多源价格采集、数据清洗、指数计算、MySQL 存储、接口服务、ECharts 可视化。
|
||
> 目标:轻量落地、成本可控、模块解耦、可迭代扩容。
|
||
|
||
**语言与职责(已定稿)**:
|
||
|
||
- **Python:仅负责采集层**(多源适配器、调度、反爬、raw/队列表落库),**不承担**管理后台、对外 API、治理批处理、指计算。
|
||
- **Java:全栈后台**(**Spring Boot** 为主):治理与标准化、指数字典与批算、REST API、鉴权/审计、**管理端后端**、数据库迁移(**Flyway / Liquibase** 等,推荐与 Java 工程同仓)。
|
||
- **集成面**:**MySQL 为事实源**;Python 写 raw(及约定内字段),Java 服务消费 raw 后写 clean、指标与维表。跨语言不共享运行时,**只共享表结构契约与(可选)OpenAPI/内部 HTTP**。
|
||
|
||
---
|
||
|
||
## 1. 设计目标与原则
|
||
|
||
| 目标 | 说明 |
|
||
|------|------|
|
||
| 轻量优先 | 单机或少量节点即可闭环 MVP,避免过早引入重中间件 |
|
||
| 可演进 | 原始层、治理规则、指算法、接口契约版本化,支持重算与追溯 |
|
||
| 解耦 | 按数据源隔离适配器,采集/治理/计算/服务异步与边界清晰 |
|
||
| 风险可控 | 反爬、去重、异常、合规与审计可落实在流程与表结构上 |
|
||
|
||
**原则**:原始数据以追加为主;指数计算与抓取失败解耦;对外服务读「指标/维表」而非直扫原始大表(必要时走缓存/汇总表)。
|
||
|
||
---
|
||
|
||
## 2. 总体分层架构
|
||
|
||
| 层级 | 职责 | 轻量实现形态 |
|
||
|------|------|----------------|
|
||
| 采集层 | 多源页面/API 抓取、任务调度、反爬与频控 | Python 爬虫 + APScheduler 或 Cron |
|
||
| 接入/缓冲层 | 削峰、解耦、失败重试 | Redis List/Stream,或队列表 + 多 Worker |
|
||
| 治理层 | 去重、单位/品种归一、异常与质量打标 | **Java 定时任务**(如 Spring `@Scheduled` / XXL-Job 等)+ 可配置规则/维表 |
|
||
| 计算层 | 指数据模型、基期、权重、链式/环比等 | **Java 批算**,输入输出可幂等、可版本化 |
|
||
| 存储层 | 原始明细、清洗数据、指数结果、元数据 | **MySQL 8.0+(主库,推荐 InnoDB + utf8mb4)** |
|
||
| 服务层 | REST API、鉴权、限流、健康检查、**管理后台 API** | **Spring Boot**(可 Undertow/Tomcat;网关按组织规范) |
|
||
| 展示层 | 图表、看板、运营侧配置界面 | 前端 + ECharts,**与 Java API 联调** |
|
||
|
||
**存储说明**:优先复用**现成 MySQL 实例**;大对象/审计快照可接 MinIO/对象存储。热查询与去重可叠加 **Redis**(可选)。
|
||
|
||
---
|
||
|
||
## 3. 技术选型
|
||
|
||
| 领域 | 建议 | 备注 |
|
||
|------|------|------|
|
||
| 爬虫 | **Python**:`httpx` / `aiohttp` + `selectolax` / `parsel`;强渲染页用 Playwright | 仅 `ingest` + `adapters/`,**禁止**在爬虫工程实现指算法/管理端 |
|
||
| 爬虫调度 | Cron 或 Python 内 APScheduler、独立 worker 进程 | 与 **Java 治理/指计算** 在时间上解耦(可 raw 落库后由 Java 轮询或消息触发) |
|
||
| 主库 | **MySQL 8.0+** | Java 与 Python **共用**(凭 DSN/账号分离读写权限更佳);InnoDB、utf8mb4、分区同前 |
|
||
| 后台与批算 | **Java 17+**,**Spring Boot 3.x** | 治理/指数/REST/管理端一体;批处理可用 Spring 自带任务或接调度平台 |
|
||
| 表结构迁移 | **Flyway** 或 **Liquibase**(**放在 `server/` 工程**) | Python 不持有权威 DDL,按迁移脚本对齐 |
|
||
| 缓存/去重 | Redis | Java 与(可选)Python 爬虫**共用**时,**键命名空间**须约定,避免互踩 |
|
||
| API 契约 | 对外/前后端以 **OpenAPI** 为单一描述(可 `server` 生成 spec) | Python 侧不暴露业务 API 时,可不建 OpenAPI |
|
||
| 前端 | Vue 或 React + ECharts | 请求 **Java 后端** |
|
||
| 可观测 | 统一标准:双栈**结构化日志** + trace id(可 OpenTelemetry) | Java/Python 用不同 app name 区分 |
|
||
| 部署 | Docker Compose:`java` 服务 + `crawl-worker` 镜像**分离**;扩容再上编排 | |
|
||
|
||
---
|
||
|
||
## 4. 模块解耦
|
||
|
||
```
|
||
各数据源适配器 (Source A/B/…N)
|
||
│
|
||
▼
|
||
统一采集契约:RawRecord + 来源元数据 (source_id、抓取时间、指纹等)
|
||
│
|
||
▼
|
||
原始层(raw,只增不改或软删策略)
|
||
│
|
||
▼
|
||
治理与标准化(品种/市场/单位/别名映射、质量 flag)
|
||
│
|
||
▼
|
||
指数计算引擎 ←── 基期/权重/算法版本配置
|
||
│
|
||
▼
|
||
指标结果表 + 维表
|
||
│
|
||
▼
|
||
对外/管理 API(Java)/ ECharts
|
||
```
|
||
|
||
- **源适配器(Python)**:一源一包;**仅** 输出统一 raw 契约;选择器、登录、限频不混入 Java 代码。
|
||
- **治理/指数/服务(Java)**:消费 raw 表,**写** clean 与 `index_series` 等,对外提供 REST 与**运营配置能力**。
|
||
- **指数字典与算法版本**:`index_code`、`config_version` 等字段在 **Java 域模型与 Flyway** 中一以贯之。
|
||
- **计算幂等**:由 Java 批任务保证,按「业务日期 + 指数版本」可重算。
|
||
|
||
---
|
||
|
||
## 5. 数据流(端到端)
|
||
|
||
1. 调度触发各源任务,拉取列表/详情。
|
||
2. 写入 **raw**(可含 `LONGTEXT`/`JSON` 与关键检索列),记录 HTTP 状态与内容指纹。
|
||
3. **去重(Java 治理)**:DB 唯一约束在 Flyway 中统一定义;Redis 去重与 Java/爬虫约定 key 前缀。
|
||
4. **标准化(Java)**:单位、市场、品种别名 → 维表由 **管理端/导入** 维护。
|
||
5. **异常与质量(Java)**:写 clean 与 `quality_flag`。
|
||
6. **指数批算(Java)**:读清洗 + 当版本配置 → 写 `index_series`。
|
||
7. **API(Java)**:读指标与维表;**管理端** 对规则、维表、重算任务 **发令**;缓存键与索引策略在 Java 侧实现。
|
||
|
||
---
|
||
|
||
## 6. MySQL 实践要点
|
||
|
||
- 版本与字符集:MySQL **8.0+**,`utf8mb4`,InnoDB。
|
||
- 半结构化:JSON 存扩展字段,**高筛选字段尽量落普通列+索引**;8.0 可配合生成列建索引。
|
||
- 大表:按**交易日期/入库日期** RANGE 分区,便于归档与冷数据迁出。
|
||
- 多 Worker 队列表:使用 `SELECT … FOR UPDATE SKIP LOCKED`(8.0)实现任务抢占。
|
||
- 备份:定期全量/增量,raw 与指标层策略与 RPO 对齐业务要求。
|
||
|
||
与 PostgreSQL 相比:若未来有极重 JSON 分析或强 GIS 需求,可再评估从库/分析库;当前以结构化为主的指数平台,**单 MySQL 可覆盖 MVP 至中期**。
|
||
|
||
---
|
||
|
||
## 7. 反爬、质量与合规
|
||
|
||
| 主题 | 建议 |
|
||
|------|------|
|
||
| 反爬 | 频控、退避、UA/协议合法范围内轮换;**优先**对接官方或开放 API。 |
|
||
| 去重 | DB 唯一约束 + 指纹/哈希;同日多价按产品规则(如中位、最新)在治理层定稿。 |
|
||
| 异常 | 硬规则(负值、超阈)→ 统计(分位/IQR)→ 业务产季/地域规则。 |
|
||
| 指算法 | 固定文档:公式、基期=100、缺权/缺价处理、品种替代;多版本可并存。 |
|
||
| 合规 | robots/条款与用途评估;**最小化**存个人类信息;对公示指数附方法论与免责声明。 |
|
||
|
||
**生产环境长期自动化采集**:除上表原则外,须满足《[数据源上线清单](./数据源上线清单.md)》中的 **授权归档、SLA、观测告警、降级与对外话术** 等上线门禁;与《产品设计-蔬菜价格指数平台》中 `source_code`、来源透传约定一致。
|
||
|
||
---
|
||
|
||
## 8. 主要风险与缓解
|
||
|
||
| 风险 | 缓解 |
|
||
|------|------|
|
||
| 源站改版 | 监控解析失败率;适配器独立;**原始 HTML/快照** 可选入对象存储 |
|
||
| 数据损坏/误删 | raw 追加策略、备份、指数可重算 |
|
||
| 单库压力 | 分区、读写分离(从库只读 API)、热数据 Redis |
|
||
| 指争议与口径变更 | 配置版本化、**可重放**历史计算、对账报表 |
|
||
|
||
---
|
||
|
||
## 9. 分阶段迭代
|
||
|
||
| 阶段 | 内容 | 验收参考 |
|
||
|------|------|----------|
|
||
| 阶段 1(MVP) | 1~2 个稳定源、Python 写 raw、Java 最小治理 + 1 套指数、**Spring Boot** + ECharts | 日任务成功率、指口径可说明、API P95 |
|
||
| 阶段 2 | 多源、Java 管理维表/规则、多指数、Redis、监控、指数重算入口 | 维表覆盖率、重算耗时、跨语言可观测 |
|
||
| 阶段 3 | 消息驱动、抓取/计算**水平扩展**、复杂地理层级、SLA | 扩展与容灾指标 |
|
||
|
||
---
|
||
|
||
## 10. 扩展预留
|
||
|
||
- 表设计早期包含:`source_id`、`config_version`、**时间维度** 便于分区。
|
||
- **表结构以 Java 侧迁移为权威**;若 Python 需 Pydantic 模型,**由 DDL/OpenAPI/手动同步**,避免两真相。
|
||
- Java **API 无状态**,配置与密钥走 Spring Profile / 环境变量/中心配置。
|
||
- Python 爬虫**无状态**、可水平扩展,仅依赖 DSN 与 `PYTHONPATH`/`pip install -e .`。
|
||
|
||
---
|
||
|
||
## 11. 文档维护
|
||
|
||
- 表结构、指数口径与**对外方法论文档**应与本文件同步更新(可拆子文档:`docs/指数字典与口径.md` 等)。
|
||
- **生产采集门禁**与运维基线见《[数据源上线清单](./数据源上线清单.md)》;变更授权或 SLA 时同步评审本文件第 7 节是否需修订。
|
||
- 重大架构变更时递增本文「版本」并记录变更摘要。
|
||
|
||
**版本摘要(1.2)**:增补对《数据源上线清单》的引用,明确上线环境持续采集的文档门禁。
|
||
|
||
---
|
||
|
||
## 12. 项目目录结构(与实现对齐)
|
||
|
||
**双栈单仓**(**Python 仅爬取**,**Java 为后台主工程**):
|
||
|
||
```text
|
||
fao/
|
||
├── docs/ # 架构、口径、运维说明
|
||
├── server/ # **Java 主工程**(Spring Boot 3 + Maven;`pom.xml` 在根下)
|
||
│ # `mvn -f server/pom.xml spring-boot:run`;迁移:`src/main/resources/db/migration/`
|
||
├── src/
|
||
│ └── fao/ # **仅** Python 采集包(pyproject 可 `pip install -e .`)
|
||
│ ├── common/ # 爬虫子进程:配置、日志
|
||
│ ├── db/ # 仅向 raw/队列写入所需的最小数据访问
|
||
│ └── ingest/
|
||
│ └── adapters/ # 各数据源一模块
|
||
├── scripts/ # 爬虫/批抓入口(`python -m` 等),**不含**指计算
|
||
├── config/ # 爬虫用 `.env.example` 等
|
||
├── db/
|
||
│ └── migrations/ # **可选**:与 `server` 中 Flyway **二选一为权威**;建议以 `server/.../db/migration` 为准后此处留空或删
|
||
├── tests/
|
||
│ ├── unit/ # 建议拆:server 内 surefire + Python pytest 各管各
|
||
│ └── integration/
|
||
├── web/ # 前端,对接 **Java API**
|
||
├── deploy/ # 含 `docker-compose`:服务 `app`(Java) + `crawl`(Python) 等
|
||
├── pyproject.toml # 仅 fao 爬虫包
|
||
├── setup.py
|
||
└── .gitignore
|
||
```
|
||
|
||
| 路径 | 放什么、不放什么 |
|
||
|------|------------------|
|
||
| `server/` | 全部 **HTTP、安全、域模型、批治理、指计算、迁移**;**不** 写爬虫。 |
|
||
| `src/fao/ingest` | 抓取、限频、**仅 raw/队列** 模型与写入;**禁止** 指算法与维表管理。 |
|
||
| `src/fao/db` | 爬虫用 DAO;表结构**消费** Java Flyway 产出。 |
|
||
| `scripts` | 只调度 **Python 爬取**;**指数重算/治理** 入口放在 **Java** 或企业调度调 Java 接口。 |
|
||
| `config` | 以爬虫侧为主;Java 用 `server` 内 `application-*.yml`。 |
|
||
| `web` | 管理端/大屏,BFF 为 **Java**。 |
|
||
|
||
**可选**:`e2e/` 联调;`ops/` 运维脚本。前端可拆独立仓库,**`server` 为后台边界**。
|
||
|
||
---
|
||
|
||
## 13. 语言边界与数据所有权
|
||
|
||
| 项目 | 约定 |
|
||
|------|------|
|
||
| **表结构** | **Java 工程迁移脚本** 为唯一权威;Python 升级前 **拉取最新** DDL 或从文档生成模型。 |
|
||
| **raw 写入** | 仅 **Python**(或经审查的 ETL)写到约定列;**不** 写 clean/指数字段,除非团队明确开例外并文档化。 |
|
||
| **clean / 指标** | 仅 **Java**(及经同一鉴权的运维脚本)。 |
|
||
| **同一 Redis** | key 加前缀:`crawl:` / `app:` 等。 |
|
||
| **重算指数** | 管理端/定时器调 **Java** 内部服务或 `POST /internal/...`(**内网 + 鉴权**)。 |
|
||
| **故障** | 爬虫挂 → raw 空;**Java 报表仍可展示**历史指数据;**治理/批算**在 Java 侧可告警「无新 raw」。 |
|
||
|
||
---
|
||
|
||
*本文档为蔬菜价格指数平台的技术架构说明,实现细节以各模块设计与数据库 DDL 为准。*
|