feat(data-ana): docker 本地测试通过 + gRPC 修复 + nextstep 上下游依赖文档
This commit is contained in:
@@ -319,47 +319,53 @@
|
||||
|
||||
### 2.6 data-ana(Python/FastAPI,P4)
|
||||
|
||||
| 场景 | 技术/规则 |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 学情诊断 | ClickHouse 宽表查询,5s 内返回 |
|
||||
| CDC 链路 | Debezium 监听 MySQL binlog → Kafka(`edu-cdc.next_edu_cloud.<table>`)→ DataAna 消费写 ClickHouse |
|
||||
| CDC 延迟监控 | Debezium 暴露 lag metrics,超阈值告警 |
|
||||
| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) |
|
||||
| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) |
|
||||
| CDC 消费者实现 | `cdc_consumer.py` 用 aiokafka AIOKafkaConsumer,lifespan 启动 asyncio.create_task 后台运行 |
|
||||
| ClickHouse 写入 | `clickhouse_client.upsert_student_dashboard()` 用 client.insert() 写宽表,client 为 None 时降级返回 False |
|
||||
| Debezium 事件解析 | `before/after/source/op/ts_ms` 五字段,op=r(快照)/c(新增)/u(更新)/d(删除) |
|
||||
| 多表关联缓存 | 内存 ExamCache 缓存 exam_id→class_id 映射(来自 core_edu_exams CDC 事件),grades 事件触发时查缓存填充宽表 class_id |
|
||||
| Consumer offset 重置 | `kafka-consumer-groups --reset-offsets --to-earliest --execute` 需先停消费者让 group 处于 Empty 状态 |
|
||||
| 5 张宽表对齐 | student_dashboard_view / student_errors / mastery_snapshot / attendance_logs / ai_usage_log(ai-allocation §5 强制) |
|
||||
| ActionState 信封对齐 | Python 服务 `{success, data, degraded}` 必须改 `{success, data, error:{code, message, details?, traceId?}}`,degraded 作 details 子字段(004 §11.5 P0) |
|
||||
| gRPC server 启用 | P4 启用 data-ana gRPC server 端口 50055(004 §4.2 矩阵),HTTP 3006 保留作 Gateway 直连降级 |
|
||||
| DataScope 6 级过滤 | Repository 层注入 WHERE(SELF/CLASS/GRADE/SCHOOL/DISTRICT/ALL),iam `GetEffectiveDataScope` gRPC P4 补全(004 §15.3 #5) |
|
||||
| Outbox 豁免 | 派生数据事件(MasteryUpdated / WarningTriggered / AIUsageRecorded)豁免 Outbox,允许直接 Kafka producer(004 §12.2 + §15.3 #6) |
|
||||
| 掌握度算法 | WEIGHTED_MOVING_AVG(w_i=0.6^i)+ FORGETTING_CURVE(半衰期 30 天)取 max,近 5 次不足降级 SIMPLE_AVG |
|
||||
| 预警阈值设计 | LOW_MASTERY(<0.4) / CRITICAL_LOW(<0.2) / SCORE_DROP(下降≥20%) / ABSENT_FREQUENT(≥3次/周) / TREND_DECLINE(连续3次降) |
|
||||
| 预警去重 | 同一 (target_id, warning_type) 24h 内只触发一次,Redis 位图 `data_ana:warning:{target_id}:{type}:{date}` TTL 25h |
|
||||
| CDC 水平扩展 | P4 单实例 + 内存 ExamCache LRU → P6 多实例 + Redis ExamCache + partition 扩容 + HPA |
|
||||
| CDC 手动 commit | v2 改进:`enable_auto_commit=False` 手动 commit(at-least-once),commit 前确保 ClickHouse 写入成功 |
|
||||
| ClickHouse 容量规划 | 5 宽表年增量 ~2.3GB(5000 学生×20 次/月),单节点可承载;P6 按 class_id hash 分片 + ReplicatedMergeTree |
|
||||
| ClickHouse FINAL | ReplacingMergeTree 查询必须加 `FINAL` 或用 `argMax` 聚合,否则读到重复版本(clickhouse_repository.py 已实现 P0 整改) |
|
||||
| 缓存键命名 | `data_ana:datascope:{user_id}` 5min / `data_ana:exam:{exam_id}` 30day / `data_ana:dedup:{event_id}` 7day |
|
||||
| 4 端 Dashboard | teacher/student/parent/admin 各自 Dashboard API + 权限点 ANALYTICS_*_DASHBOARD(v2 新增 4 端权限) |
|
||||
| SubscribeMasteryUpdate | gRPC server-streaming RPC 为 P5+ AI 个性化推荐预留实时推送通道(analytics.proto v2 提案) |
|
||||
| GDPR 数据删除 | ClickHouse 无 UPDATE/DELETE,用 `ALTER TABLE ... DELETE WHERE student_id=?` 异步 mutation(P6 实现) |
|
||||
| 字段脱敏 | student_id 在日志中 hash 化(structlog processor),PII 不入日志 |
|
||||
| 事件版本化 | 事件 payload 加 `schema_version: "v1"` 字段,破坏性变更发 v2,consumer 双消费 6 个月过渡 |
|
||||
| API 版本化 | HTTP `/analytics/v1/*` + gRPC `next_edu_cloud.analytics.v1`,破坏性变更新增 v2,旧版保留 6 个月 |
|
||||
| Projection 演进 | P4 CDC 直连 → P4 后期双消费(CDC + 领域事件)→ P6 ClickHouse MaterializedView 自动聚合 |
|
||||
| 测试策略 | pytest + Testcontainers(ClickHouse/Kafka 容器化)+ 单元 ≥80% + 集成 ≥60% + 契约 pact-python + E2E |
|
||||
| Dockerfile 多阶段 | builder(uv sync --frozen)+ runtime(python:3.12-slim < 200MB)+ HEALTHCHECK 集成 |
|
||||
| 配置项完整清单 | pydantic-settings 30+ 配置项,env_prefix="DATA_ANA_",含 ClickHouse/Kafka/iam gRPC/Redis/OTel/算法参数/预警阈值 |
|
||||
| 降级模式 4 场景 | ClickHouse/Kafka/Redis/iam gRPC 不可用时返回骨架 + `error.details.degraded=true`,4 错误码 DATA_ANA_*_UNAVAILABLE |
|
||||
| Python gRPC 实现 | grpc.aio + betterproto + ServerInterceptor 透传 W3C trace context,与 HTTP 共享同一 Application Service |
|
||||
| structlog API | 24.x 用 `make_filtering_bound_logger(level)`,旧版 `make_filtering_logger` 已废弃 |
|
||||
| ruff per-file-ignores | gRPC servicer 方法必须 PascalCase(N802 忽略)/ FastAPI Depends 在参数默认值(B008 忽略),pyproject.toml 配 per-file-ignores |
|
||||
| Mock 数据脚本 | `scripts/data-ana-mock-data.sql` 提供 5 宽表演示数据(20 学生×2 考试×3 知识点),用 `now()-INTERVAL` 避免硬编码日期 |
|
||||
| Python 3.12+ 现代化 | ruff UP042 用 StrEnum 替代 `str, Enum` / UP046 用 PEP 695 类型参数 `class Foo[T]` 替代 `Generic[T]` |
|
||||
| 场景 | 技术/规则 |
|
||||
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 学情诊断 | ClickHouse 宽表查询,5s 内返回 |
|
||||
| CDC 链路 | Debezium 监听 MySQL binlog → Kafka(`edu-cdc.next_edu_cloud.<table>`)→ DataAna 消费写 ClickHouse |
|
||||
| CDC 延迟监控 | Debezium 暴露 lag metrics,超阈值告警 |
|
||||
| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) |
|
||||
| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) |
|
||||
| CDC 消费者实现 | `cdc_consumer.py` 用 aiokafka AIOKafkaConsumer,lifespan 启动 asyncio.create_task 后台运行 |
|
||||
| ClickHouse 写入 | `clickhouse_client.upsert_student_dashboard()` 用 client.insert() 写宽表,client 为 None 时降级返回 False |
|
||||
| Debezium 事件解析 | `before/after/source/op/ts_ms` 五字段,op=r(快照)/c(新增)/u(更新)/d(删除) |
|
||||
| 多表关联缓存 | 内存 ExamCache 缓存 exam_id→class_id 映射(来自 core_edu_exams CDC 事件),grades 事件触发时查缓存填充宽表 class_id |
|
||||
| Consumer offset 重置 | `kafka-consumer-groups --reset-offsets --to-earliest --execute` 需先停消费者让 group 处于 Empty 状态 |
|
||||
| 5 张宽表对齐 | student_dashboard_view / student_errors / mastery_snapshot / attendance_logs / ai_usage_log(ai-allocation §5 强制) |
|
||||
| ActionState 信封对齐 | Python 服务 `{success, data, degraded}` 必须改 `{success, data, error:{code, message, details?, traceId?}}`,degraded 作 details 子字段(004 §11.5 P0) |
|
||||
| gRPC server 启用 | P4 启用 data-ana gRPC server 端口 50055(004 §4.2 矩阵),HTTP 3006 保留作 Gateway 直连降级 |
|
||||
| DataScope 6 级过滤 | Repository 层注入 WHERE(SELF/CLASS/GRADE/SCHOOL/DISTRICT/ALL),iam `GetEffectiveDataScope` gRPC P4 补全(004 §15.3 #5) |
|
||||
| Outbox 豁免 | 派生数据事件(MasteryUpdated / WarningTriggered / AIUsageRecorded)豁免 Outbox,允许直接 Kafka producer(004 §12.2 + §15.3 #6) |
|
||||
| 掌握度算法 | WEIGHTED_MOVING_AVG(w_i=0.6^i)+ FORGETTING_CURVE(半衰期 30 天)取 max,近 5 次不足降级 SIMPLE_AVG |
|
||||
| 预警阈值设计 | LOW_MASTERY(<0.4) / CRITICAL_LOW(<0.2) / SCORE_DROP(下降≥20%) / ABSENT_FREQUENT(≥3次/周) / TREND_DECLINE(连续3次降) |
|
||||
| 预警去重 | 同一 (target_id, warning_type) 24h 内只触发一次,Redis 位图 `data_ana:warning:{target_id}:{type}:{date}` TTL 25h |
|
||||
| CDC 水平扩展 | P4 单实例 + 内存 ExamCache LRU → P6 多实例 + Redis ExamCache + partition 扩容 + HPA |
|
||||
| CDC 手动 commit | v2 改进:`enable_auto_commit=False` 手动 commit(at-least-once),commit 前确保 ClickHouse 写入成功 |
|
||||
| ClickHouse 容量规划 | 5 宽表年增量 ~2.3GB(5000 学生×20 次/月),单节点可承载;P6 按 class_id hash 分片 + ReplicatedMergeTree |
|
||||
| ClickHouse FINAL | ReplacingMergeTree 查询必须加 `FINAL` 或用 `argMax` 聚合,否则读到重复版本(clickhouse_repository.py 已实现 P0 整改) |
|
||||
| 缓存键命名 | `data_ana:datascope:{user_id}` 5min / `data_ana:exam:{exam_id}` 30day / `data_ana:dedup:{event_id}` 7day |
|
||||
| 4 端 Dashboard | teacher/student/parent/admin 各自 Dashboard API + 权限点 ANALYTICS_*_DASHBOARD(v2 新增 4 端权限) |
|
||||
| SubscribeMasteryUpdate | gRPC server-streaming RPC 为 P5+ AI 个性化推荐预留实时推送通道(analytics.proto v2 提案) |
|
||||
| GDPR 数据删除 | ClickHouse 无 UPDATE/DELETE,用 `ALTER TABLE ... DELETE WHERE student_id=?` 异步 mutation(P6 实现) |
|
||||
| 字段脱敏 | student_id 在日志中 hash 化(structlog processor),PII 不入日志 |
|
||||
| 事件版本化 | 事件 payload 加 `schema_version: "v1"` 字段,破坏性变更发 v2,consumer 双消费 6 个月过渡 |
|
||||
| API 版本化 | HTTP `/analytics/v1/*` + gRPC `next_edu_cloud.analytics.v1`,破坏性变更新增 v2,旧版保留 6 个月 |
|
||||
| Projection 演进 | P4 CDC 直连 → P4 后期双消费(CDC + 领域事件)→ P6 ClickHouse MaterializedView 自动聚合 |
|
||||
| 测试策略 | pytest + Testcontainers(ClickHouse/Kafka 容器化)+ 单元 ≥80% + 集成 ≥60% + 契约 pact-python + E2E |
|
||||
| Dockerfile 多阶段 | builder(uv sync --frozen)+ runtime(python:3.12-slim < 200MB)+ HEALTHCHECK 集成 |
|
||||
| 配置项完整清单 | pydantic-settings 30+ 配置项,env_prefix="DATA_ANA_",含 ClickHouse/Kafka/iam gRPC/Redis/OTel/算法参数/预警阈值 |
|
||||
| 降级模式 4 场景 | ClickHouse/Kafka/Redis/iam gRPC 不可用时返回骨架 + `error.details.degraded=true`,4 错误码 DATA_ANA_*_UNAVAILABLE |
|
||||
| Python gRPC 实现 | grpc.aio + betterproto + ServerInterceptor 透传 W3C trace context,与 HTTP 共享同一 Application Service |
|
||||
| structlog API | 24.x 用 `make_filtering_bound_logger(level)`,旧版 `make_filtering_logger` 已废弃 |
|
||||
| ruff per-file-ignores | gRPC servicer 方法必须 PascalCase(N802 忽略)/ FastAPI Depends 在参数默认值(B008 忽略),pyproject.toml 配 per-file-ignores |
|
||||
| Mock 数据脚本 | `scripts/data-ana-mock-data.sql` 提供 5 宽表演示数据(20 学生×2 考试×3 知识点),用 `now()-INTERVAL` 避免硬编码日期 |
|
||||
| Python 3.12+ 现代化 | ruff UP042 用 StrEnum 替代 `str, Enum` / UP046 用 PEP 695 类型参数 `class Foo[T]` 替代 `Generic[T]` |
|
||||
| ClickHouse argMax 别名冲突 | `argMax(col, calculated_at)` 与同 SELECT 的 `max(calculated_at) AS calculated_at` 冲突(ILLEGAL_AGGREGATION),别名改为 `latest_calculated_at` |
|
||||
| Python proto 生成 | `grpc_tools.protoc` 生成 `_pb2_grpc.py` 用 `import xxx_pb2`(flat),放入 package 需改为 `from generated_proto import xxx_pb2` |
|
||||
| Dockerfile PYTHONPATH | Python 服务 `src/generated_proto/` 作为包导入时,Dockerfile 需 `ENV PYTHONPATH="/app/src"`,否则 `ModuleNotFoundError: generated_proto` |
|
||||
| OTel 日志噪声 | OTel collector 不可达时刷屏 `Transient error ... retrying`,设 `OTEL_TRACES_EXPORTER=none` + `OTEL_METRICS_EXPORTER=none` + `OTEL_LOGS_EXPORTER=none` 静默 |
|
||||
| Docker Hub 不可达 | Dockerfile base image 用标准名 `python:3.12-slim`,本地 `docker pull docker.m.daocloud.io/library/python:3.12-slim` 后 `docker tag` 为标准名 |
|
||||
| ruff per-file-ignores | 自动生成的 proto 代码(`src/generated_proto/*.py`)在 `pyproject.toml` 配 `per-file-ignores = ["ALL"]`,避免 lint-staged 传单文件时 `extend-exclude` 无效 |
|
||||
|
||||
### 2.7 messaging(TS/NestJS,P5)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user