proxy-pool/README.md
2026-08-07 15:46:10 +08:00

345 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Proxy Pool
## 项目定位
面向多供应商动态代理资源的集中控制、独占提取与高并发转发平台。
Proxy Pool 将供应商接入、短 TTL 代理活动池和管理状态集中到 Controller
同时提供三类边界清晰的访问方式。集群 `100,000 QPS` 是设计目标,尚未经过
代表性环境的负载测试证明。
## 项目背景
不同代理供应商的响应格式、鉴权方式、计费方式和代理存活时间并不一致;部分
代理只有几十秒有效期。系统还需要同时处理容量预留、健康状态、路由切换、
一次性提取和高并发转发。
Proxy Pool 用 Controller 协调这些变化,并让 Gateway 数据面只消费节点内存中的
不可变快照,避免在转发热路径查询 PostgreSQL、Redis 或 Provider API。
## 使用方式
- **Gateway**:调用方连接平台,由平台选择上游代理并转发 HTTP 或 HTTPS
CONNECT。`proxy-gateway` 已装配本地监听、指标探针和控制面 Register/Watch/ACK/
Runtime/Outcome 会话;带凭据 Proxy 的分发与当前内存 View 已闭环。
- **Distribution**:调用方按条件提取真实代理;成功提取即独占消费,不支持归还、
续租或状态查询。
- **Admin**:运维人员查询状态、启停 Upstream、切换 Routing并触发严格配置
重载。Admin 与 Distribution 已由 Controller 运行在独立监听器上。
## 核心能力
- **严格配置**:配置版本 1、YAML 未知字段拒绝、Secret 引用解析、公开监听保护、
引用和容量边界校验。
- **Provider 获取与协调**Provider HTTP Client、响应上限、模板解析、分布式
Leader/请求额度、退避和补池 Supervisor 已接入 Controller。
- **Redis 活动池**:短 TTL Proxy Upsert、去重、健康更新、Worker ownership、
库存、过期清理、Distribution 幂等与分布式限流均使用原子操作或有界流程。
- **Distribution**:支持 `partial` / `allOrNothing`、TTL 与健康过滤、Gateway
库存预留,以及 `AVAILABLE -> EXTRACTED` 的一次性独占提取。
- **Controller 入口**`proxy-controller` 已装配 Distribution、Admin、Metrics、
PostgreSQL 迁移与 Redis 活动池,并支持联动优雅停机。
- **Checker 指标**Metrics 启用时暴露 Checker 任务下发与 Observation 接受/拒绝计数;
标签仅使用固定检查级别和结果,不记录 Proxy、IP、URL 或凭据。
- **Gateway 指标**Metrics 启用时暴露代理尝试的固定阶段成功/失败计数、本地
Outcome 队列满后的丢弃计数、HTTP/CONNECT 已接收请求数与在途数,以及活跃
CONNECT 隧道数;协议标签仅有 HTTP 与 CONNECT不记录 Proxy、路由、目标、
客户端或凭据。
- **Drain 指标**Controller 暴露 `proxy_pool_controller_drain_candidates_total`
`proxy_pool_controller_drains_started_total``reason` 仅有 `unhealthy`
`upstream_disabled`,不包含 Proxy、Worker、Upstream、会话或地址。
- **Provider 指标**Controller 暴露
`proxy_pool_controller_provider_fetch_results_total{class}`
`proxy_pool_controller_provider_valid_candidates_total`
`proxy_pool_controller_provider_new_proxies_total``class` 仅有 `valid`
`empty`、`duplicate_only`、`error`,不包含 Upstream、Proxy 或错误文本标签。
- **提取指标**Controller 暴露
`proxy_pool_controller_extraction_requests_total{result}`
`proxy_pool_controller_extraction_requested_proxies_total`
`proxy_pool_controller_extraction_returned_proxies_total``result` 仅有完成、
部分、空、库存不足、幂等冲突、限流、不可用、无效与内部错误等固定枚举,
不包含 Client、请求、过滤条件、Upstream、Proxy 或错误文本标签。
- **容量指标**Controller 在既有 Provider 库存对账周期聚合托管 Proxy、可用/有效
Slot、待拉取数量和活跃上游数并暴露
`proxy_pool_controller_capacity_inventory_reads_total{result}``result` 仅有
`success``error`,不按 Upstream、Worker、Proxy、会话或地址拆分。
- **PostgreSQL 管理面**持久化配置版本、Upstream/Routing 管理状态、Admin
审计与 Outbox不保存 Proxy 明细或逐次提取记录。
- **Gateway 组件**HTTP 正向代理、HTTPS CONNECT、双向 Tunnel、重试、超时、
目的地址保护、本地快照存储、容量调度和 Worker 控制面 Register/Watch/ACK/
Runtime/Outcome 会话组件已有实现与定向测试。每次代理尝试只向本地有界队列写入
Outcome微批确认失败会重发同一序列队列满时丢弃观测样本不阻塞转发请求。
`SessionSupervisor` 会为可恢复控制面中断执行
有界退避重连。Controller 可向 Worker 下发已归属 Proxy、Gateway Routing 与按引用去重的
凭据材料快照。Gateway 会将 Routing、Proxy 与凭据原子编译为同一内存 View并只按当前未过期
View 匹配请求,并在内存中按 Sequential、Random、Round Robin、Weighted 或 Least
Connections 选择上游。无候选时支持 reject、受 `waitTimeout` 限制的本地容量等待,
以及仍经过目标地址策略的 direct`proxy-gateway` 通过独立控制面拨号地址维护
Session并在每份 Snapshot 有效期的一半前接收版本递增的完整刷新;仅在持有未过期
Snapshot 时 Ready凭据材料只保留在当前节点内存 View。Controller 内成功提交的
Upstream 启停、Routing 切换和配置发布会向本进程全部在线 Worker 快照流广播刷新;
定时刷新仍作为跨进程收敛与失效保护。
- **Gateway 粘性会话**:可为已认证请求启用 X-Proxy-Session 等配置 Header
Gateway 以 Client、Routing 和会话值派生本地有界绑定,成功建连后固定到同一
Proxy并在代理失效、快照移除或转发失败时自动重绑。原始会话值不会写入日志、
指标、PostgreSQL 或 Redis也不会向目标站点转发。
- **安全边界**Gateway、Distribution 与 Admin 使用各自的认证语义,并支持
CIDR、可信代理、严格请求解析和敏感信息最小化Admin 的读写权限与
Distribution 提取权限可按命中凭据分别收敛。Distribution 凭据还可限制单次
提取数量、可访问 Upstream 与地区Gateway 凭据可限制可访问 Routing未配置时
保持既有全范围行为。Gateway 凭据还可在每个 Worker 内限制每分钟请求数,
同时限制 HTTP 请求和 CONNECT 隧道的并发数;热路径计数不写入 Redis 或
PostgreSQL。
## 架构概览
```mermaid
flowchart LR
Client[调用方] -->|HTTP / CONNECT| Gateway[proxy-gateway]
Client -->|独占提取| Distribution[Distribution]
Operator[运维人员] -->|管理操作| Admin[Admin]
subgraph CP[proxy-controller 已运行]
Distribution --> Controller[Controller]
Admin --> Controller
end
Controller -->|Fetch| Provider[Provider API]
Controller --> Redis[(Redis)]
Controller --> PostgreSQL[(PostgreSQL)]
Checker[Checker<br/>任务协议已接入] -. Observation .-> Controller
Controller -. gRPC Snapshot .-> Gateway
```
- **PostgreSQL** 只保存管理面状态,不保存 Proxy 明细或逐次提取记录。
- **Redis** 保存短 TTL Proxy 活动池、Provider 协调、Distribution 幂等与分布式
限流等可重建的短期状态。
- **Gateway 热路径** 只读取节点内存,不查询 PostgreSQL、Redis 或 Provider API。
## 当前完成度
截至 **2026-08-02**,实施计划中可直接勾选的检查项为 **60 / 7481.1%**。详情见
[实施计划](docs/development/implementation-plan.md)和
[交付完成度审计](docs/requirements/completion-audit.md)。
- **已完成**严格配置、Provider 获取与协调、Redis 活动池、Distribution 原子
提取与限流、Controller 的 Admin/Distribution/Metrics 监听,以及 PostgreSQL
管理状态与有界审计查询WorkerControlPlane 的 Register、Snapshot ACK、Runtime 心跳接收和
Redis 会话栅栏,以及 Gateway Outcome 上报的有界队列、序列确认与重试;
Controller 的 Redis 共享 BASIC/EGRESS/TARGET 检查任务、按上游的有界轮转调度、HTTP/HTTPS/SOCKS5
Checker 探测和
Observation 状态归并Provider 连续空结果的代次化自动 Sequential 切换、禁用候选过滤、
末端 `stop` 的 CAS 路由停用和 Snapshot 即时刷新。
- **部分完成**Docker Compose/Kubernetes 运行时 mTLS Overlay。
- **待完成**:故障演练和代表性集群压测;现有 HTTP、CONNECT 长连接和 Extract
场景只提供可复现的负载工具,不构成容量验证结论。
检查项数量不等于生产就绪度。静态部署清单与 protobuf descriptor 验证也不代表
端到端拓扑已经完成;`100,000 QPS` 仍只是待验证的集群设计目标。
## 快速开始
前置条件为 Go 1.26、PowerShell以及用于 fixture 测试的 Docker。先在当前
PowerShell 会话设置本地占位凭据;这些值仅用于本地验证:
```powershell
$env:PROXY_POOL_GATEWAY_PASSWORD = "LOCAL_GATEWAY_PASSWORD"
$env:PROXY_POOL_EXTRACT_TOKEN = "LOCAL_EXTRACT_TOKEN"
$env:PROXY_POOL_ADMIN_TOKEN = "LOCAL_ADMIN_TOKEN"
$env:PROVIDER_A_TOKEN = "LOCAL_PROVIDER_A_TOKEN"
$env:PROVIDER_B_TOKEN = "LOCAL_PROVIDER_B_TOKEN"
$env:PROXY_POOL_CONFIG_FINGERPRINT_KEY = "LOCAL_HIGH_ENTROPY_KEY_AT_LEAST_32_BYTES"
```
`PROXY_POOL_CONFIG_FINGERPRINT_KEY` 在 Admin 启用时必须至少为 32 字节,并与业务
Secret 分离管理。
校验配置并执行仓库验证:
```powershell
go run ./deploy/tools/configcheck deploy/config/local.yaml
./scripts/verify.ps1
```
Redis、PostgreSQL 和 Controller fixture 脚本会使用 Docker 启动隔离依赖:
```powershell
./scripts/test-redis.ps1
./scripts/test-postgres.ps1
./scripts/test-controller.ps1
```
PostgreSQL 与 Redis 是 Controller 的启动依赖。当前可运行的 Controller 入口如下,
`CONFIG_FILE` 替换为实际配置路径,并确保其中的 PostgreSQL 与 Redis 地址可从
进程所在网络访问:
```powershell
go run ./cmd/proxy-controller -config CONFIG_FILE
```
[`deploy/config/local.yaml`](deploy/config/local.yaml) 面向 Compose 网络,默认使用
服务名 `postgres``redis`;它可直接用于配置校验,但宿主机执行 `go run` 时需要
改用宿主机可达的存储地址。
本地配置中的 `.invalid` Provider URL 是故障演示占位,不会提供真实代理。
Gateway 已提供启动命令;需要先启用 Controller `controlPlane` 并配置匹配的 mTLS
证书(回环 fixture 可使用明文),再提供独立的拨号地址和 Worker 身份:
```powershell
go run ./cmd/proxy-gateway -config CONFIG_FILE `
-control-plane CONTROLLER_HOST:8443 `
-cluster-id CLUSTER_ID -worker-id WORKER_ID `
-instance-id INSTANCE_ID -zone ZONE
```
以上参数也可通过 `PROXY_POOL_CONTROL_PLANE_ADDRESS`、`PROXY_POOL_CLUSTER_ID`、
`PROXY_POOL_WORKER_ID`、`PROXY_POOL_INSTANCE_ID` 与 `PROXY_POOL_ZONE` 提供。
Gateway 的 `/livez`、`/readyz`、`/metrics` 使用配置中的 `metrics.listen`;无有效
Snapshot 时 `/readyz` 返回 `503`。Checker 使用独立的逻辑/实例身份拉取有界任务:
```powershell
go run ./cmd/proxy-checker -config CONFIG_FILE `
-control-plane CONTROLLER_HOST:8443 `
-checker-id CHECKER_ID -instance-id INSTANCE_ID `
-max-in-flight 64
```
Checker 的参数也可通过 `PROXY_POOL_CONTROL_PLANE_ADDRESS`
`PROXY_POOL_CHECKER_ID`、`PROXY_POOL_CHECKER_INSTANCE_ID` 与
`PROXY_POOL_CHECKER_MAX_IN_FLIGHT` 提供。它不会访问 Redis/PostgreSQL生产
Controller 在启用控制面时装配 Redis 共享任务 broker并按启用的 Upstream 调度
HTTP/HTTPS/SOCKS5 BASIC 检查、按每个 `check.urls` 创建 EGRESS 任务,并按启用 Routing 的
`check.targets` 创建 TARGET 任务。调度监督器每轮读取已发布配置,因此 reload 后的上游/路由启停、
检查间隔、抖动、超时、重试次数、`maxInFlight`、EGRESS URL 和 TARGET Profile 都会在下一轮生效;
BASIC、EGRESS 与 TARGET 以有界轮转组共享上游并发上限。新启用的上游无需重启 Controller。
EGRESS 对成功响应提取纯文本 IP 或常见 JSON IP 字段并将其作为全局健康事实回传TARGET 事实
仅归并到对应的 `(routing_name, target_url)` Profile不改变 Proxy 全局健康。
`proxy-loadgen` 的 HTTP 场景可按固定请求数或固定时长运行,并将 HTTPS 目标经
Gateway 的请求交给标准 HTTP Transport 建立 CONNECT
```powershell
go run ./cmd/proxy-loadgen `
-target https://TARGET_URL/health `
-proxy http://GATEWAY_HOST:8080 `
-requests 10000 -concurrency 128 -timeout 10s
```
CONNECT 长连接场景直接向 HTTP Gateway 发起隧道握手,并在成功后保持每条隧道指定
时长;`-timeout` 只限制 TCP 连接与 CONNECT 响应,`-hold` 控制建连后的保持时间:
```powershell
go run ./cmd/proxy-loadgen `
-scenario connect -target https://TARGET_URL/ `
-proxy http://GATEWAY_HOST:8080 `
-requests 1000 -concurrency 128 -timeout 5s -hold 30s
```
使用 `-duration 30s -rate 5000` 可运行限速场景;省略 `-rate` 时固定数量 worker
会饱和发送。普通 HTTP 场景中,`-method`、重复的 `-header``-body` 可组合用于
Distribution 的提取接口:
```powershell
go run ./cmd/proxy-loadgen `
-target http://CONTROLLER_HOST:8081/api/v1/proxies/extract `
-method POST -header "Content-Type: application/json" `
-header "X-API-Key: DISTRIBUTION_API_KEY" -body '{"count":1}' `
-requests 1000 -concurrency 32 -timeout 10s
```
`extract` 场景则自动构造 POST 请求、每请求独立的 `X-Request-ID`
`Idempotency-Key`,并只校验响应的 `requestId`、数量和同响应内代理 ID 唯一性:
```powershell
go run ./cmd/proxy-loadgen `
-scenario extract `
-target http://CONTROLLER_HOST:8081/api/v1/proxies/extract `
-header "X-API-Key: DISTRIBUTION_API_KEY" `
-requests 1000 -concurrency 32 -timeout 10s `
-extract-count 1 -extract-fulfillment partial
```
命令输出 JSON 报告,包含成功/失败分类、`TunnelsEstablished`、
`ExtractResponsesValidated`、`ExtractReturned`、`ExtractValidationFailures`、`RateStartsGenerated`、
`RateStartsDropped`、固定内存的
连接握手延迟分位上界、吞吐和 Go 运行时内存/GC 快照。它不会输出提取响应中的地址或
凭据,也不构成 100,000 QPS 证明。限速场景以 `Requests` 表示实际发起数;当
`RateStartsDropped` 非零时,目标速率受压测端并发容量或时间窗限制,报告吞吐不得
标注为已达到配置的 `-rate`
需要保留单次容量证据时,使用 `-output REPORT_FILE` 同时写出 JSON 文件;文件在
同目录完整写入后才替换目标,标准输出仍保留相同报告,便于交给日志或指标系统。
## 关键配置与入口
- [本地配置](deploy/config/local.yaml)与
[完整配置参考](docs/configuration/reference.md)
- [Distribution API](docs/api/distribution.md):默认本地入口
`http://127.0.0.1:8081`
- [Admin API](docs/api/admin.md):默认本地入口 `http://127.0.0.1:8082`
- Controller Metrics默认本地入口 `http://127.0.0.1:9090`,提供 `/livez`
`/readyz``/metrics`
- [控制面协议](docs/api/control-plane.md)Worker/Checker 的 protobuf 契约与
已验证的有界任务领取/Observation 上报边界
- [运维手册](docs/operations/runbook.md):依赖、探针、发布边界与故障处置
## 核心不变量
1. Gateway 热路径只读节点内存,不访问 PostgreSQL、Redis 或 Provider API。
2. Proxy 容量使用 `Reserved -> Active` 原子转换,禁止超卖。
3. Distribution 成功时原子执行 `AVAILABLE -> EXTRACTED`;提取后不归还、不续租。
4. `pool.maxSize` 是当前未提取库存硬上限;`fetch.maxTotal` 是 Redis generation
内的累计获取停止阈值。
5. CONNECT 向客户端提交 `200` 后不透明重放。
6. 公开监听必须有认证或 CIDR 访问保护。
7. PostgreSQL 只保存管理面状态Proxy 明细只存在于 Redis 短 TTL 活动池和节点
内存Redis 中的短期状态可由 Provider 重建。
## 路线图
- **P0 - Worker 控制面闭环**Worker session、Snapshot ledger、ACK、运行态接收、
ownership 索引,以及 Gateway 快照客户端。
- **P0 - Checker 健康链**BASIC/EGRESS/TARGET 的共享调度、实际探测、Observation reducer 和
`FETCHED -> AVAILABLE / SUSPECT / UNHEALTHY` 状态链已完成;可配置的持续 UNHEALTHY
回收会自动为已分配项发起带健康/归属栅栏的 Drain待 Snapshot ACK 与运行态归零后再删除;
TARGET 事实按路由目标 Profile 独立归并。
- **P1 - Gateway 与 Routing**Gateway 进程、快照凭据分发、五种 Routing 策略与
`onUnavailable` 已接入;上游停用会从后续完整 Snapshot 排除,并对现有 Worker
ownership 发起带策略 revision 栅栏的 Drain。Routing 切换立即刷新在线 Worker 的
完整快照Sequential 的新分配切到新上游,旧 Proxy 与既有连接自然排空。动态容量
调整仍待完成。
- **P1 - 可观测与部署**:低基数业务指标、完整 Compose/Kubernetes 进程拓扑,
以及故障转移和恢复演练。
- **P2 - 容量证明**`proxy-loadgen`、HTTP/CONNECT/Extract 分场景压测,以及
可复现的代表性 `100,000 QPS` 集群报告。
## 文档导航
**设计与需求**
- [产品设计](docs/design/product-design.md)
- [总体架构](docs/design/architecture.md)
- [项目结构](docs/design/project-structure.md)
- [需求追踪](docs/requirements/traceability.md)
- [架构决策记录](docs/adr/README.md)
**开发与 API**
- [开发指南](docs/development/guide.md)
- [实施计划](docs/development/implementation-plan.md)
- [配置参考](docs/configuration/reference.md)
- [Distribution API](docs/api/distribution.md)
- [Admin API](docs/api/admin.md)
- [控制面协议](docs/api/control-plane.md)
**部署与运维**
- [本地配置](deploy/config/local.yaml)
- [运维手册](docs/operations/runbook.md)
- [生产就绪检查](docs/operations/production-readiness.md)
- [安全模型](docs/security/security-model.md)
**审计与测试**
- [交付完成度审计](docs/requirements/completion-audit.md)
- [测试策略](docs/testing/strategy.md)
- [详细测试策略](docs/testing/test-strategy.md)
- [故障注入](docs/testing/failure-injection.md)