第 4 篇 · 4.1 动态视角
学习目标
完成本节后,你将能够:
- 用同一套模板阅读 tRPC、Public API、ingestion、worker 和 events query。
- 区分同步返回路径、异步 job 路径和查询路径。
- 判断一次改动应该落在入口层、契约层、执行层还是状态层。
4.1.1 静态架构回答“有什么”,动态架构回答“怎么动”
第 3 篇讲了 web、packages/shared、worker 和状态层。第 4 篇关心的是运行时:
- 请求从哪个入口进入;
- 哪个 wrapper 建立 auth 和 tenant scope;
- 哪个 schema 把外部输入变成内部数据;
- 是否需要投递队列;
- worker 消费后如何处理失败;
- 最终读写哪个存储;
- 指标和日志在哪里打。
读 Langfuse 的运行链路时,建议按下面的模板做笔记。
| 维度 | 要问的问题 | Langfuse 示例 |
|---|---|---|
| 入口 | 谁调用?HTTP、tRPC、BullMQ 还是内部函数? | Browser 调 tRPC;SDK 调 Public API;worker 消费 Redis job。 |
| 可信边界 | 这里的身份和租户上下文怎么建立? | protectedProjectProcedure、createAuthedProjectAPIRoute、queue payload 的 authCheck.scope.projectId。 |
| 契约 | 输入输出由什么 schema/type 约束? | Zod request/response、QueueJobs、FilterState、domain schema。 |
| 执行 | 这里做业务逻辑,还是只做协调? | ingestion processor 负责协调 S3/Redis/secondary queue,业务 merge 在 IngestionService。 |
| 状态 | 读写 Postgres、ClickHouse、Redis 还是 S3? | raw event 进 S3,job 进 Redis,最终 record 进 ClickHouse。 |
| 失败 | 失败后是同步返回、重试、降级还是记录指标? | API 返回统一错误;BullMQ retry;S3 SlowDown 进入 secondary queue;writer 记录 rows_dropped。 |
4.1.2 五条主链路总图
这张图里有三种箭头:
- 同步请求箭头:UI/tRPC/Public API 需要给调用者返回结果。
- 异步数据箭头:ingestion 接收后把重工作交给 queue + worker。
- 查询箭头:UI filter 最终 lower 成 ClickHouse SQL。
4.1.3 五条链路分别学什么
| 链路 | 核心问题 | 你会学到的设计点 |
|---|---|---|
| tRPC | UI 内部请求怎么获得 project scope | procedure 工厂、session、membership、OpenTelemetry、feature flag gate。 |
| Public API | 外部 REST 契约怎么稳定 | middleware、API key access level、rate limit、Zod response、Fern。 |
| Ingestion | 高频事件为什么不直接写 ClickHouse | S3 raw event、轻量 job、producer/consumer path contract、sampling。 |
| Worker queue | 后台任务如何统一执行 | WorkerManager、BullMQ options、wait/processing metrics、failed/error/stalled。 |
| v4 events query | 宽事件表怎么高效查询 | events_core/events_full、field set、FilterState lowering、split query。 |
4.1.4 一条链路的“源码读法”
以 Public API POST /api/public/traces 为例,不要只看 route 文件。按顺序看:
web/src/pages/api/public/traces/index.ts:这个 endpoint 暴露什么 HTTP 方法。withMiddlewares.ts:CORS、method dispatch、error boundary、ClickHouse route tag。createAuthedProjectAPIRoute.ts:API key、access level、rate limit、query/body/response schema。web/src/features/public-api/types/**:外部请求/响应契约。processEventBatch.ts:如果是写入事件,它如何进入 ingestion 数据面。queues.ts:异步 job 载荷是不是跨部署兼容。worker/src/queues/ingestionQueue.ts:consumer 如何读取 job 并处理失败。
同样的套路可以套到任何链路。
4.1.5 初学者最容易误读的地方
| 误读 | 正确理解 |
|---|---|
web 就是前端 | web 同时包含 Next.js UI、tRPC、Public API、ingestion HTTP 入口。 |
shared 是 utils | shared 是跨运行时契约中心,尤其是 queue/schema/filter/query。 |
| worker 只是在跑 job | worker 还统一处理 metrics、error、stalled、secondary queue、ClickHouse writer。 |
| Redis 保存事件 | Redis 保存 job 和短期缓存;raw event 在 S3/blob。 |
| ClickHouse 只有一张 events 表 | v4 读写围绕 events_full 和 events_core,legacy 表还在迁移期共存。 |
| FilterState 是 UI 状态 | FilterState 是 UI、API、query builder 共享的过滤语言。 |
4.1.6 本篇阅读顺序
建议按顺序读:
- tRPC 链路:理解 UI 内部 API 的 procedure 工厂和 project scope。
- Public API 链路:理解外部契约、API key、限流和 Fern。
- Ingestion 链路:理解高吞吐写入为什么拆成 S3 + queue + worker + ClickHouse。
- Worker 队列:理解后台执行器、重试、指标和隔离。
- v4 Events 查询:理解 FilterState 如何变成高性能 ClickHouse 查询。