# Proxy Pool 总体架构设计 ## 1. 范围 Proxy Pool 是多供应商代理聚合平台,同时提供: - **Gateway**:接受 HTTP/HTTPS CONNECT 请求,选择上游代理并代转发。 - **Distribution API**:一次性独占发放真实代理地址,发放后不再管理其使用。 - **Admin API**:查询状态、启停 Upstream、切换 Routing、触发重载。 - **Control Plane**:Provider 获取、健康、生命周期、容量、切换、持久化与分发。 首要容量目标是集群峰值 100,000 QPS。该数字是设计目标,必须通过后续 容量测试证明,不能由文档直接宣称实现。 ## 2. 选择的方案 采用“独立数据面 + 集中控制面 + 独立健康执行器”: ```mermaid flowchart LR C[Clients] --> LB[HAProxy / Envoy / LVS] LB --> G1[Gateway Worker] LB --> GN[Gateway Worker N] G1 --> PX[Owned Proxies] GN --> PX E[Extract Clients] --> D[Distribution API] A[Operators] --> ADM[Admin API] D --> CP[Controller] ADM --> CP CP --> PG[(PostgreSQL)] CP --> R[(Redis)] CP --> F[Provider APIs] CP --> CK[Checker Workers] CP --> G1 CP --> GN ``` ### 2.1 为什么不用单体 Provider、数据库和健康探测是不可控 I/O。把它们和 Gateway 放在一个进程, 会让供应商抖动、配置重载和数据库故障直接污染 100k QPS 热路径。 ### 2.2 为什么不全面微服务化 Provider、Pool、Routing、Distribution 在首版需要共享事务和一致性规则。 先保持 Controller 模块化单体,避免提前引入事件顺序和分布式事务;Checker 因负载特征不同独立扩缩,Gateway 因热路径独立部署。 ## 3. 进程与职责 ### 3.1 proxy-gateway - Client 认证、CIDR 访问控制和租户限流。 - HTTP 正向代理与 HTTPS CONNECT。 - 首条命中 Routing、Upstream 选择和 Proxy least-connections 选择。 - 本地原子容量预留、建连、Active 计数和结果上报。 - 本地不可变 Snapshot;热路径无数据库/Redis/Provider API。 - SSRF 与 DNS Rebinding 防护。 ### 3.2 proxy-controller - 配置加载、校验、热更新和版本管理。 - Upstream/Provider fetch Leader、独立限流、singleflight 和退避。 - Proxy 解析、去重、TTL、状态机、pool.maxSize 与 fetch.maxTotal。 - Routing Sequential 当前选择和原子切换。 - Worker 所有权/容量切片、Snapshot 发布、ACK 和重同步。 - Distribution 与 Admin HTTP 接口。 - PostgreSQL 权威状态和 Redis 短期协调。 ### 3.3 proxy-checker - 消费检查任务,执行基础连接、出口和目标级探测。 - 使用 jitter、maxInFlight、超时和分级复检。 - 只上报 Observation,不直接修改最终状态。 - Controller 的确定性 reducer 根据 Observation 更新状态。 ### 3.4 proxy-loadgen - 分别生成 HTTP QPS、CONNECT 活跃连接、建连速率和 Extract 并发。 - 输出环境、场景、延迟、错误、CPU、RSS、句柄和网络结果。 ## 4. 模块边界 ```text domain/ proxy Proxy、状态机、TTL、唯一键、容量 routing 规则、匹配、策略与 Sequential 状态 upstream Provider 能力、Fetch 分类与 Pool 需求 extraction 独占提取命令、结果和审计事实 client 认证主体、权限与限制 gateway/ ingress HTTP/CONNECT 接入 dispatch Routing、Proxy 选择、容量预留、重试资格 transport 上游连接复用、握手、隧道和失败阶段 snapshot 版本校验、后台构建和原子切换 controller/ provider Fetch 计划、限流、singleflight、错误分类 pool Pool reconcile、生命周期和所有权 health 任务计划和 Observation reducer routing Sequential 原子切换和恢复 extraction 批量原子提取与审计 distribution Worker 注册、Snapshot/Delta 与结果上报 adapters/ postgres, redis, grpc, http, provider_template ``` 领域模块不导入 HTTP、gRPC、SQL、Redis 或模板引擎。Adapter 依赖领域接口, 进程装配只发生在 `cmd`。 ## 5. Proxy 模型 ```go type Proxy struct { ID ProxyID Scheme Scheme Host string Port uint16 Username string CredentialVersion string SecretRef string SourceUpstream UpstreamID CreatedAt time.Time ExpiresAt *time.Time LastCheckedAt *time.Time LastSuccessAt *time.Time Latency time.Duration MaxConcurrency int64 State ProxyState Tags map[string]string } ``` 密码不参与日志可见唯一键;`CredentialVersion` 区分同一用户名的凭据轮换。 运行态 `active` 与 `reserved` 存在 Worker 本地、按 Proxy ID 分片,不写入 不可变 Snapshot。 ## 6. Proxy 状态机 ```mermaid stateDiagram-v2 [*] --> FETCHED FETCHED --> CHECKING CHECKING --> AVAILABLE: basic check passed CHECKING --> UNHEALTHY: check exhausted AVAILABLE --> SUSPECT: first meaningful failure SUSPECT --> AVAILABLE: recheck passed SUSPECT --> UNHEALTHY: consecutive failures AVAILABLE --> DRAINING: TTL margin / disable / ownership revoke DRAINING --> EXPIRED: no active or reserved capacity AVAILABLE --> EXTRACTED: atomic exclusive extraction UNHEALTHY --> REMOVED EXTRACTED --> EXPIRED: expires EXPIRED --> REMOVED ``` `ACTIVE` 与 `BUSY` 不是持久状态,而是容量计数。一个 AVAILABLE Proxy 可以 同时承载多个 Gateway 请求,直到有效并发上限。 ## 7. Gateway 热路径 ```mermaid sequenceDiagram participant C as Client participant G as Gateway participant D as Dispatch participant T as Transport participant P as Upstream Proxy C->>G: HTTP / CONNECT G->>G: auth + access + admission G->>D: RouteRequest D->>D: first-match route + local candidate filter D->>D: CAS reserved +1 D-->>G: Allocation G->>T: execute allocation T->>P: dial / proxy handshake alt connected T->>D: reserved -1, active +1 T-->>C: response / tunnel T->>D: active -1 + outcome else failed before commit T->>D: reserved -1 + failure G->>D: optional safe retry with exclusion end ``` ### 7.1 Dispatch 接口 ```go type Dispatcher interface { Acquire(context.Context, RouteRequest) (*Allocation, error) Commit(*Allocation) error Release(*Allocation, Outcome) } ``` 不变量: - `Acquire` 只读本地 Snapshot 与本地分片运行态。 - `Allocation` 创建前必须 CAS 预留成功。 - Commit 将 Reserved 恰好一次转换为 Active。 - 未 Commit 的失败释放 Reserved;已 Commit 的结束释放 Active。 - Release 重复调用安全但产生错误指标。 ### 7.2 重试提交点 - HTTP:响应头写给 Client 前可按方法与失败阶段重试。 - CONNECT:上游 CONNECT 成功且向 Client 写 200 后不可重试。 - GET/HEAD 默认最多尝试 2 个不同 Proxy。 - POST/PUT/PATCH/DELETE 默认不自动重试。 ## 8. Provider Fetch ```mermaid flowchart TD S[Capacity signal] --> SF{singleflight running?} SF -->|yes| W[Coalesce signal] SF -->|no| R[Read current demand] R --> L[Acquire Provider leader] L --> C{Under maxSize/maxTotal?} C -->|no| X[Stop] C -->|yes| I[Wait requestInterval] I --> M[Acquire maxInFlight] M --> API[Call Provider API] API --> CL{Classify} CL -->|error| BO[Backoff + fetchErrorCount] CL -->|empty| EC[emptyCount++] CL -->|valid| DD[Deduplicate + reset empty] DD --> HC[Create FETCHED and schedule check] ``` ### 8.1 Empty、Duplicate 与 Error - **Empty**:HTTP/认证成功、模板执行成功,解析后合法 Proxy 数为 0。 - **Duplicate-only**:合法 Proxy 数大于 0,但去重后新增为 0;不计 Empty, 重置 Empty 连续计数并增加 duplicate 指标。 - **Error**:超时、DNS、非预期 HTTP、认证、响应超限、模板或解析异常; Empty 计数保持不变,增加 Error 并进入退避。 - **Success**:至少一个合法候选;Empty 计数归零。是否最终入池由去重、 maxSize、TTL 和健康结果决定。 ## 9. Sequential 切换 ```mermaid sequenceDiagram participant F as Provider Controller participant U as Upstream Runtime participant R as Routing Runtime participant DB as PostgreSQL F->>U: EmptyFetch U->>U: consecutiveEmpty++ alt below threshold U-->>F: continue current else threshold reached U->>DB: persist depleted generation DB-->>R: notify affected routings R->>DB: CAS current index A -> B DB-->>R: switched once R->>R: old A proxies drain naturally end ``` Upstream 的 Empty 事实全局共享;每条 Routing 独立 CAS 当前索引。多个并发 协程只能有一个成功从 A 切到 B,其他协程读取新版本,不会再切到 C。 ## 10. Exclusive Extraction ### 10.1 核心流程 ```mermaid sequenceDiagram participant C as Extract Client participant API as Distribution API participant CP as Controller participant W as Gateway Worker participant DB as PostgreSQL C->>API: POST extract(count, filters) API->>API: auth + access + limits API->>CP: Extract command CP->>DB: select eligible unowned proxies alt candidate owned by Worker CP->>W: revoke and drain W-->>CP: active=0, reserved=0, ownership released end CP->>DB: transaction AVAILABLE -> EXTRACTED DB->>DB: insert extraction records DB-->>CP: committed rows CP-->>API: proxy + expiry API-->>C: requested/returned/proxies ``` ### 10.2 原子批量提取 PostgreSQL Adapter 使用事务和 `FOR UPDATE SKIP LOCKED` 选择满足以下条件的 行,并在同一事务更新状态与写审计记录: - State 为 AVAILABLE。 - 未分配 Worker 所有权,Reserved/Active 为 0。 - 剩余 TTL 不低于 `minRemainingTTL`。 - 健康检查时间不早于 `maxHealthCheckAge`。 - 提取后仍保留 `reserveForGateway`。 - 符合 protocol、region、carrier 与 allowedUpstreams。 `partial` 提交实际可得数量;`allOrNothing` 在锁定数量不足时回滚。 ### 10.3 与 Gateway 共池 Controller 优先维护两类库存:Worker-owned Gateway 容量和 unowned Extract 库存。Distribution 只直接提取 unowned Proxy。需要从 Gateway 回收时,先 发布 DRAINING、等待 Worker ACK 和容量归零,再清除所有权并提取。这样避免 Snapshot 传播延迟造成同一 Proxy 同时被 Gateway 新分配和 API 发放。 ## 11. Cluster 所有权 ```mermaid flowchart LR CP[Controller] -->|capacity lease| W1[Worker 1] CP -->|capacity lease| W2[Worker 2] P1[Proxy set A] --> W1 P2[Proxy set B] --> W2 U[Unowned extract reserve] --> CP ``` - 同一个 Proxy 同一时刻只归一个 Worker 所有。 - Worker 仅对自身 Proxy 做本地原子计数。 - 所有权租约有 epoch 与过期时间;Worker 失联后先等待租约过期,再分配给 其他 Worker,避免双主。 - Snapshot 包含 Worker 专属 Proxy 集、Routing、策略和版本校验和。 - Worker 断开控制面后在 `maxStaleAge` 内使用最后快照;超限停止接收新流量, 已有隧道排空。 ## 12. 存储与一致性 ### 12.1 PostgreSQL 权威保存:配置版本、Upstream、Proxy 生命周期、Routing 当前选择、Worker 所有权、Extraction Record、Client、审计与 outbox。 ### 12.2 Redis 保存可重建短期状态:Provider Leader 租约、分布式限流、singleflight 信号、 短期 Client 限流和 Worker 心跳。Redis 不保存唯一权威业务状态。 ### 12.3 Outbox 任何需要发布 Snapshot/事件的 PostgreSQL 状态更新同时写 outbox。发布成功 后标记;Worker 使用 epoch/version 幂等应用,缺口触发完整 Snapshot。 ## 13. 健康模型 Health Observation 包含 Proxy、检查层级、Routing/目标组、阶段、结果、延迟、 出口信息与时间。Reducer 规则: - 新 Proxy 必须通过基础检查才可 AVAILABLE。 - 第一次有意义失败进入 SUSPECT,不立即删除。 - 连续失败达到配置阈值进入 UNHEALTHY。 - SUSPECT 复检成功恢复 AVAILABLE。 - 目标级失败只影响对应 Routing/Target Profile,不直接全局删除。 - 任务按稳定哈希分散,并对 interval 加 jitter。 ## 14. 配置热更新 ```mermaid flowchart LR F[Read file] --> P[Parse] P --> V[Validate references and regex] V --> B[Build immutable config] B --> D[Diff old/new] D --> S[Atomic swap] S --> G[Drain removed tasks/resources] ``` 热更新规则: - 删除/禁用 Upstream:停止 Fetch 和新分配,已有 Proxy/连接 Drain。 - 减小 maxSize:不强杀连接,停止补池并按策略自然缩容。 - Routing 立即对新请求生效;旧请求持有旧 Snapshot 完成。 - 修改 API 地址或凭据版本会重建 Provider Adapter,但不会把错误计成 Empty。 - 新配置任何校验失败时,保留旧版本并报告完整错误。 ## 15. 安全 - Gateway、Distribution、Admin 认证相互独立。 - `auth.mode` 支持 none、usernamePassword、apiKey、ipWhitelist 与组合 any。 - `access.allowCIDRs` 独立于认证;代理头只在来源属于 trustedProxies 时接受。 - 严格模式下,非回环监听且 auth=none、allowCIDRs 为空时启动失败。 - 目的地址解析前后都拒绝 loopback、private、link-local、metadata 和配置禁区。 - Secret 使用环境变量或文件引用;日志统一 Secret 类型脱敏。 - Provider 模板只能使用白名单纯函数,禁止文件、网络和环境变量读取。 - Distribution 返回真实凭据属于 Raw 弱控制模式,文档必须明确其风险。 ## 16. 可观测性 关键指标: - Gateway 请求、连接、建连、字节、延迟、重试和拒绝。 - Routing 当前 Upstream、切换、无可用和策略选择。 - Upstream Proxy 状态、Available Slots、Active、Reserved、Pending Fetch。 - Provider Fetch 成功、Empty、Duplicate、Error、429、退避和耗时。 - Extraction requested、returned、insufficient、atomic conflict 和审计失败。 - Snapshot epoch/version、陈旧时长、应用耗时和重同步。 Prometheus 标签不包含 Proxy IP、Client ID、session、完整 URL 或 request ID。 这些信息进入受控、脱敏、可采样日志。 ## 17. 性能与容量 ### 17.1 集群容量公式 ```text worker_replicas = ceil(peak_qps / (tested_worker_qps * target_utilization)) + largest_failure_domain_replicas ``` 默认 `peak_qps=100000`、`target_utilization<=0.60`。单 Worker 能力必须在相同 CPU、内存、网络、Go 版本、配置和上游响应模型下测得。 ### 17.2 热路径预算 - Dispatch 无 I/O、无全局锁,100k Proxy Snapshot 下 p99 小于 100 微秒。 - 所有队列、buffer、重试和日志均有界。 - Listener、Client、Routing、Worker 和 Proxy 均有独立准入限制。 - 过载在路由/建连前快速拒绝,不允许请求堆积耗尽内存。 - 结果上报批量、异步、有界,不反压请求热路径。 ### 17.3 独立场景 必须分别测试:10k 稳态 QPS、100k 峰值 QPS、活跃 CONNECT、建连速率、 大 Snapshot 更新、Provider 故障、Controller 断线和 Worker 故障域丢失。 ## 18. 故障行为 | 故障 | 行为 | |---|---| | Provider 超时/500 | 计 Error、退避;不计 Empty,不影响已有 Proxy | | Provider 合法空响应 | Empty++;达到阈值触发相关 Routing 原子切换 | | Redis 不可用 | Gateway 不受影响;Controller 使用本地退化并停止高风险写操作 | | PostgreSQL 不可用 | Gateway 使用最后快照;停止 Extract 和权威状态变更 | | Controller 断线 | Worker 在 maxStaleAge 内继续;超限拒绝新流量并排空 | | Worker 崩溃 | 所有权租约过期后重新分配;期间不双重所有 | | Checker 积压 | 降低普通复检频率,优先新 Proxy 与 SUSPECT,不无限排队 | | Snapshot 缺版本 | 丢弃增量并请求完整 Snapshot | | 所有 Upstream 不可用 | 执行显式 onUnavailable,默认 reject | ## 19. 交付边界 项目架构必须包含四个命令、领域模块、Gateway/Controller/Checker 模块、 存储/协议 Adapter、OpenAPI/Proto、配置样例、Compose/Kubernetes、监控、 迁移、测试 fixture、负载场景和开发文档。目录存在但没有契约或测试不算完成。