# 对话内容提取结果 ## 权威来源 - 文件:`对话内容.md` - 大小:167,284 字节 - 行数:9,404 行 - 读取时间:2026-07-28 ## 最终产品边界 系统同时提供两种入口: 1. Gateway:系统选择上游代理并代转发 HTTP、HTTPS CONNECT,预留 SOCKS5 扩展。 2. Distribution API:一次性、独占地返回真实上游代理。代理成功提取后 立即从可分配池移除,不需要租约、释放接口或使用量追踪。 系统管理多个 Upstream Provider,并使用配置化 Routing 决定 Gateway 或 Extract 请求使用哪些 Upstream。 ## 后出现并覆盖早期建议的决策 - Distribution API 的 Lease/Release/Shared Allocation 方案被最终的 Exclusive Extraction 取代。 - `pool.maxSize` 表示当前系统维护的未提取代理硬上限;累计供应商提取额度 使用独立的 `fetch.maxTotal`,不能复用同一个字段。 - `switchAfterEmptyFetch` 只统计“上游请求成功、解析成功,但解析后没有任何 合法代理”的结果;超时、HTTP 错误、认证错误、DNS 错误和模板错误只计 `fetchErrorCount`。全重复结果不当作空结果,单独记录。 - `consecutiveEmptyFetch` 属于 Upstream;Sequential 当前选择属于 Routing。 某 Upstream 达到阈值时,引用它的 Routing 原子切换;已有代理继续耗尽。 - Extract API 默认部分满足 `partial`;也支持 `allOrNothing`。 - Extract API 从 `AVAILABLE` 原子转换到 `EXTRACTED` 后才返回,保证同一代理 永不发放两次。 ## 核心不变量 - 请求热路径不得调用 Provider API,也不得查询全量 Redis/PostgreSQL 后排序。 - 代理分配必须原子预留容量,防止并发超卖。 - 代理唯一键为 `scheme + host + port + username + credentialVersion`;日志 和指标不得暴露密码。 - TTL 优先级为响应 `expiresAt`、响应 `ttl`、配置固定 TTL;活动池中的每个 Proxy 必须有明确过期时间,内部时间统一 UTC。 - 健康检查至少区分全局健康和 Routing/目标健康,并使用抖动和并发上限。 - GET/HEAD 可按配置安全重试;非幂等方法默认不自动重试;CONNECT 建立后 不透明重放。 - 默认不直连;所有 Upstream 不可用时必须显式选择 reject、wait 或 direct。 - Gateway、Distribution、Admin、Metrics 使用独立监听和认证/访问控制。 - 非回环监听且无认证、无 CIDR 保护时,严格模式必须拒绝启动。 ## 可观测性边界 - Provider 拉取结果只按固定 `class` 聚合:`valid`、`empty`、`duplicate_only`、`error`。 指标分别记录拉取结果次数、合法候选数和新入池代理数,不按 Upstream、Proxy、IP、错误 文本或凭据拆分。 - Provider 指标已完成。 - Extraction 只按固定 `result` 聚合请求:`complete`、`partial`、`empty`、 `insufficient`、`idempotency_conflict`、`rate_limited`、`unavailable`、`invalid`、 `error`。计数只记录请求数量和响应交付数量;幂等重放不被误记为新的 Proxy 消费。 - Extraction 指标已完成。 - 容量指标由 Provider 库存对账循环采样,聚合 managed、available、effective、pending 与 active Upstream Gauge。Upstream ID 仅保留在 Controller 进程内用于聚合替换和 任期结束清理,不输出为 Prometheus 标签;读取结果只允许 `success` 或 `error`。 - 容量指标已完成。 - 进程级致命错误统一经 JSON `slog` 输出;敏感属性、错误对象、URL 用户信息和查询 Secret 在写出前脱敏。该出口只用于生命周期边界,不在 Gateway/Distribution 请求热路径 逐条写日志。 ## 集群与性能 - 用户补充:高峰可能达到 100,000 请求/秒。 - 数据面采用多 Worker,本地不可变代理快照和本地容量计数。 - 同一代理必须由单个 Worker 所有,或由控制面下发容量切片;禁止每请求 访问 Redis 做全局并发计数。 - 控制面集中 Provider 获取、独立限流、singleflight、Leader 选举和快照分发。 Proxy 明细只进入 Redis TTL 活动池,不写 PostgreSQL。 - 副本数必须由单 Worker 实测能力、目标利用率和故障域余量计算。 ## 配置模型 顶层包含:`version`、`defaults`、`security`、`gateway`、`distribution`、 `admin`、`metrics`、`storage`、`routing`、`upstreams`。 每个 Upstream 包含:`enabled`、`exposure`、`provider`、`api`、`proxyAuth`、 `pool`、`capacity`、`lifecycle`、`fetch`、`check`。 Routing 自上而下匹配,首条命中停止;支持 Gateway 与 Extract 两种 purpose, 策略至少包括 sequential、random、roundRobin、weighted、leastConnections。 ## 必测场景 - 连续 4 次空后成功不得切换;连续 5 次空只切换一次。 - 100 个并发缺池请求只触发有限次 Provider fetch。 - 并发切换不能从 A 一次跳到 C。 - 并发 fetch 不得突破 pool.maxSize 或 fetch.maxTotal。 - TTL safety margin 内不得分配。 - Gateway 与 Extract 共享池时不得容量超卖或重复提取。 - 配置原子热更新期间请求不中断。 - Provider 超时不得计入 Empty Fetch。 - 重复代理不得重复入池,也不得触发空结果切换。 - 所有 Upstream 不可用时按显式策略执行。 ## Checker 与健康归并事实(2026-07-29) - Checker 是可水平扩展的事实采集进程,只执行有截止时间的探测并上报 Observation;只有 Controller Reducer 能改变权威 Proxy/目标健康状态。 - 健康层级为 BASIC、EGRESS、TARGET。新 Proxy 必须先完成全局基础检查;目标级 失败只影响对应 Routing/Target Profile,不得把全局仍健康的 Proxy 淘汰。 - 全局状态规则固定为:首次有意义失败进入 SUSPECT,连续失败达到 `maxConsecutiveFailures` 后进入 UNHEALTHY,复检成功恢复 AVAILABLE。 - 调度不能为每个 Proxy 建立常驻 goroutine 或无界队列;必须稳定分散任务、加入 jitter、限制 `maxInFlight`,并按 FETCHED、SUSPECT、AVAILABLE 的顺序优先。 - 现有 `activitypool.HealthStore.ApplyHealth` 只能提交最终状态、时间和延迟,不能 原子维护连续失败计数或目标级 Profile;新模块需要将“纯 Reducer 决策”和 “活动池原子提交”分离,通过公用窄接口复用 Memory/Redis 行为契约。 - `api/proto/controlplane/v1/controlplane.proto` 已定义 CheckTask、CheckLevel 和 HealthObservation,后续 Go 领域类型必须保持字段语义一致,但不直接依赖生成的 transport 类型。 ## 健康故障移除实现(2026-08-02) - `health.GlobalState` 和 Redis 活动记录都保存首次 `UNHEALTHY` 时间;后续 `UNHEALTHY -> CHECKING -> UNHEALTHY` 复检不会重置,成功恢复 `AVAILABLE` 才清除。 - `check.unhealthyRemoveAfter` 默认 `0s`,按 Effective Check 为每个启用 Upstream 生效。Controller Reaper 读取当前配置快照,以固定批次调用公用 `activitypool.UnhealthyRemover`,不进入 Gateway 热路径。 - Redis 使用 `{activity}` 槽内的有序索引扫描到期候选;Memory 参考实现保持相同 `SweepUnhealthy` 契约。两者只删除无 Worker ownership 的记录;仍归属 Worker 的 候选会延后一秒并在受限回执中返回 assignment epoch 与首次异常时间。Reaper 通过公共 条件式 Drain 再次核验记录仍为 `UNHEALTHY`、时间戳和归属均未变化,才创建 Ticket。 - Health、Upsert、Extract、Ownership、状态读取与过期清理脚本都在同一原子删除边界 维护该索引。Memory 单测、Redis 集成契约和配置/Reaper 单测覆盖恢复、阈值、延后与 Drain/ACK 后清理。 ## Worker Snapshot 刷新(2026-08-02) - `OwnedSnapshotSource` 每次调用只构建一份权威完整快照;新增 `RefreshingSnapshotSource` 以有效期一半为上限持续调用该窄接口,并传递最新版本和 checksum。短于全局 `maxStaleAge` 的 Proxy/ownership 租约会进一步缩短下一次刷新间隔。 - gRPC Handler 每次成功发送完整 Snapshot 都重置服务端到期计时器;流中刷新失败或中断时 Gateway 使用既有 `SessionSupervisor` 退避重连。Gateway 本地 Store 对从完整 Snapshot 消失且仍有 Active/Reserved 的 Proxy 已按 draining 继续上报。Redis Runtime 替换已在当前 session ACK、Ticket 屏障和完整零计数同时成立时自动释放 owner;Reaper 已对持续 `UNHEALTHY` 的已归属代理调用条件式 Drain。配置停用也会发布有效的上游策略视图, 从上游 owned 索引有界选择候选;候选携带上游 revision,Redis 在创建 Drain Ticket 前 再次核验策略仍停用、Proxy source、Worker 和 assignment epoch,避免旧候选跨启停使用。 - Snapshot 的 `version` 是同一 Worker 流的连续序列,`ownership_epoch` 是独立且只能前进 的权威栅栏;epoch 变化不重置 version。此前 Gateway 错把 epoch 前进要求为 version=1, 与 Controller 的 `lastAppliedVersion+1` 生成规则冲突,现已用连续版本规则统一。 - `SnapshotRefreshBroker` 是 Controller 进程内的公共扇出接口:已提交的 Upstream 启停、 Routing 切换和配置发布各向每条本地 Worker 流发送一个可合并刷新信号。Routing 不记录 单代理归属,不能按单条 Routing 对共享 Upstream 强制 Drain;Sequential 切换只改变后续 新分配,旧 Proxy/连接由原有 Snapshot 与运行态自然排空。其他 Controller 副本继续在 固定刷新周期内读取 PostgreSQL 权威管理态并收敛。 ## Drain Ticket(2026-08-02) - 原有 `BeginDrain` 只会把 Proxy 从 Worker 下发索引移除,无法让后续步骤区分“已发起 撤销”与“Gateway 已收到排除该 Proxy 的完整快照”。现已增加按 Worker 有界读取的持久化 Ticket,并以 RequiredSnapshotEpoch 强制下一份权威快照至少跨过 Drain 操作。Handler 在 Snapshot 引用登记成功后才绑定屏障,绑定内容包含 session、version、epoch 与 checksum。 - Ticket 不能单独成为释放依据。`replace_report` Lua 已把 Ticket 屏障、当前 session ACK 和完整 Runtime 替换中的零计数置于同一原子边界;只有报告缺失该 Proxy 或其 Active/Reserved 均为零时,才删除 owner/Ticket/Drain 索引并恢复可分配状态。 ## Git 同步事实(2026-07-29) - PostgreSQL 管理面基础文档已提交为 `7951c29`。 - 推送远端时返回 `Authentication failed`;没有重复相同失败操作,本地提交保持 完整,待 Git 凭据恢复后同步。 ## Routing/Sequential 验收审计(2026-07-29) - 五种 Routing 策略已有独立领域实现和单测,但 `Rule` 未携带 Strategy/ OnUnavailable,Gateway 仍把全部 Upstream 交给一个全局轮询 Dispatcher;配置 策略尚未影响真实请求链。 - `onUnavailable` 已严格校验 reject/wait/direct,但 Gateway 无候选时统一返回 503;wait/direct 和默认 reject 尚未形成运行时闭环。 - Sequential 当前版本 CAS 只在单进程内生效,构造时总从第一个 Upstream 开始; PostgreSQL 管理态尚未接入游标恢复和跨实例 CAS。 - 对话最终语义已确认并落实:单 Upstream Sequential 启动校验失败, `endBehavior` 省略时默认 `stop`;disabled candidate 的运行时推进仍待实现。 ## Proxy Capacity 验收审计(2026-07-29) - 每个 Proxy ID 已有独立打包原子计数,固定 Max 下 1,000 并发不会超卖;这满足 当前 Gateway 热路径的基本预留不变量。 - Reservation 已补齐 Cancel、重复终结、错误顺序和并发 Commit/Cancel/Release 的领域测试;Gateway 仍会忽略 Release/Cancel 错误,尚无低基数不变量观测 seam。 - `SetMax` 与 counters 分离更新;降到当前占用以下时会出现 overcommitted 状态, 需要先确定“拒绝降容”或“允许排空”的正式契约。 - Snapshot Store 永久保留见过的 Proxy ID 对应 Capacity;短 TTL、高换 IP 场景下 需要排空后回收,避免运行态注册表长期增长。 ## Routing Runtime 设计输入(2026-07-29) - `对话内容.md` 最后一个明确结论将 Sequential `endBehavior` 默认设为 `stop`; 领域构造器、配置校验与配置参考现已统一,且拒绝单 Upstream Sequential。 - 产品安全默认已确定为 `onUnavailable=reject`,但当前严格配置要求字段必填; 需要确认省略时自动补 reject,还是继续拒绝启动。 - disabled Upstream 的确定语义是停止 Fetch、健康检查和新分配,已有 Proxy/连接 自然排空;Sequential 是临时跳过还是永久推进尚需固化。 - `direct` 对 Gateway 表示经过目标安全策略后绕过代理直连;对独占提取没有可返回 的 Proxy,推荐在 `purpose=extract` 时拒绝 `direct` 配置。 - 推荐采用混合 Routing Runtime:Controller 维护 Sequential 权威游标和跨实例 CAS;Gateway 消费不可变路由快照并在本地执行 random/roundRobin/weighted/ leastConnections,热路径不访问 Redis/PostgreSQL;Distribution 在 Controller 内复用同一编译策略并交给 Redis 原子提取执行。 - Gateway 当前没有完整进程装配,`RulesRouter` 仅存在于领域/Handler 测试; Distribution 只接受调用方 `allowedUpstreams`,尚未把 `purpose=extract` 路由与 Client 许可集求交。 ## 架构证据一致性审计(2026-07-29) - `.github/workflows/ci.yml` 已覆盖 Windows/Linux vet、unit、build 及 Linux race; `scripts/verify.ps1` 也覆盖格式、vet、60 秒单测、条件 race 和 build,因此两项 基础工程验收应计为完成。 - OpenAPI 通过 Go 测试执行结构/路径/响应码检查,Protobuf descriptor 曾手工编译 通过,但 CI 没有安装/调用 `protoc`;“在 CI 编译 descriptor”仍未完成。 - `cmd/proxy-gateway`、`proxy-controller`、`proxy-checker`、`proxy-loadgen` 均不存在; Docker/Kubernetes 当前只是目标拓扑,不能作为构建产物或运行验证证据。 - Provider Reconciler 的合并通知和请求约束已完成,但没有 Redis Leader Adapter 或多实例锁测试;FETCH-004 仍是未完成要求。 - Checker/Health 当前只有配置、Protobuf 和 Proxy 状态迁移骨架,没有 Scheduler、 Reducer 或目标 Profile;HEALTH-001..003 不能记为已实现。 - 当前没有 Prometheus 指标模块或描述符测试;OBS-001 只有文档约束,运行时证据 缺失。 - Available Slots 当前纳入 AVAILABLE、TTL、安全余量、Max、Active、Reserved; ownership、route/target health 与 Gateway reserve 尚未进入同一聚合模型。 ## 文档可执行性审计(2026-07-29) - 文档相对链接人工扫描通过,但原先缺少持续门禁;新增 `docs` 包契约测试,统一 扫描 README、docs、deploy 与 diagrams,防止链接随文件调整后失效。 - 配置参考曾把尚不存在的 `cmd/proxy-controller` 作为推荐启动命令;现改为真实 可执行的 `deploy/tools/configcheck`,并明确生产 Controller 入口仍是计划能力。 - 文档中的具体 `go run`/`go build` 目标现在必须存在;包含 `...` 的通配包命令 由 Go 工具链自身解析并在完整验证中执行。 ## PostgreSQL 18 Fixture 审计(2026-07-29) - 集成 Compose 已新增仅绑定 `127.0.0.1:15432` 的 `postgres:18-alpine` 服务, 数据目录挂载为 `/var/lib/postgresql` tmpfs,不声明测试持久卷。 - Redis 测试脚本现使用专属 Compose 项目并只启动 Redis,防止未来加入的 PostgreSQL fixture 被无关测试启动或清理。 - 生产 `docker-compose.yml` 仍把 PostgreSQL 18 命名卷挂在旧路径 `/var/lib/postgresql/data`;直接修改可能影响已有本地数据,必须配套迁移步骤后 单独处理,当前不能把生产持久化拓扑视为已验证。 ## 控制面 mTLS 身份审计(2026-08-07) - X.509-SVID 叶证书承载单一工作负载身份;Gateway/Checker 启动时的自动身份派生与 Controller 授权必须都拒绝包含多个 URI SAN 的证书,即使其中只有一个 URI 与当前角色匹配。 - 解析边界已统一到 `workerruntime.SingleSPIFFEIdentity`:精确匹配信任域、环境、角色和 标识符,拒绝用户信息、端口、查询、片段、转义路径、额外 URI SAN 与跨角色复用。 - TLS 校验可能形成多条等价验证链;授权只检查同一叶证书一次,不能将链路数误判为多个身份。 - Windows 本地 Go 运行环境为 `CGO_ENABLED=0` 且没有 C 编译器;Docker Engine 可用,但 `golang:1.26-bookworm` 与 `debian:bookworm-slim` 未缓存,Docker Desktop HTTPS 代理也不可用。 因此已完成 Go 全量、Compose 静态和 Kustomize 静态验证,容器端到端验证仍待具备镜像网络的环境。 ## OpenAPI 契约审计(2026-08-07) - Go 结构契约继续锁定本地引用、operationId、响应与认证引用;额外以固定 `@redocly/cli@2.25.4` 对两份 OpenAPI 3.1 文档执行标准验证,避免只依赖自定义遍历器。 - 标准最小规则集中的 Tag 描述已提升为错误;Distribution、Health、Status、Audit、Upstreams、 Routing 和 Configuration 标签均有面向 API 使用者的稳定说明。 ## Docker 构建上下文审计(2026-08-07) - Dockerfile 构建阶段需要 `COPY . .`,因此根 `.dockerignore` 是隔离本地工作区与镜像上下文的 必要边界。它显式排除 Git 元数据、环境文件、本地配置、控制面证书/私钥、PEM/CRT/KEY 文件及 构建和测试产物;部署测试会在清单被删除或放宽时失败。 ## 部署静态门禁审计(2026-08-07) - GitHub Actions 的 Deployment job 在同一组无敏感值 fixture 下渲染生产 Compose、测试 Compose 以及 Kubernetes base/development mTLS Overlay,并对两份配置运行严格校验;它仍不替代 需要镜像网络和真实服务的容器端到端测试。