proxy-pool/docs/configuration/reference.md
youfak 2166214777
Some checks are pending
ci / proto (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run
ci / race (push) Waiting to run
ci / integration (push) Waiting to run
feat: schedule routing target health checks
2026-08-02 09:16:46 +08:00

531 lines
22 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.

# 配置参考
## 1. 加载规则
主配置格式为 YAML根字段 `version` 当前固定为 `1`。加载器启用严格字段
检查,拼写错误或未来版本字段不会被静默忽略。当前仓库可使用与未来进程启动
相同的严格加载器校验本地部署配置:
```powershell
go run ./deploy/tools/configcheck deploy/config/local.yaml
```
Controller 入口已实现,源码运行方式为:
```powershell
go run ./cmd/proxy-controller -config CONFIG_FILE
```
配置路径优先使用 `-config`,未提供时读取 `PROXY_POOL_CONFIG`。启用 Admin 时还
必须设置 `PROXY_POOL_CONFIG_FINGERPRINT_KEY`,值为至少 32 字节的独立高熵密钥;
所有 Controller 副本必须一致,且该密钥不得放入 YAML 或 PostgreSQL。该入口已装配
PostgreSQL 管理面迁移、Redis 活动池、Distribution/Admin 独立监听与优雅停机;
Controller Metrics 独立监听、`/livez`、`/readyz` 和基础 Prometheus 运行时指标;
Provider 自动补池、分布式配额、动态重载和 Admin 低基数统计已装配。Gateway 进程、
Worker 控制面会话、Snapshot 凭据分发和 Snapshot 就绪探针已装配Checker 的任务流与
租约契约已接入控制面,但 Redis 共享调度、独立执行器与代表性负载验证仍在后续实施范围。
所有时间值使用 Go duration例如 `500ms`、`30s`、`5m`。示例中的
`${TOKEN}`、`${PASSWORD}`、`${POSTGRES_URL}` 等由加载器从同名环境变量
展开;这些值不得写入日志、指标、配置转储或错误响应。生产配置优先使用
环境变量或 Secret 文件,不提交明文值。
加载与热更新必须遵循同一顺序:
```text
读取文件 -> 严格解析 -> 字段校验 -> 引用/正则校验
-> 构建不可变快照 -> 计算差异 -> 原子替换
```
新配置任何一步失败时保留旧快照。删除或禁用 Upstream 只停止新 Fetch 和新
分配,已有连接进入 Drain不强制中断。
当前 Controller 启动时使用同一份不可变快照完成存储、入口和提取策略装配。
Admin 重载会原子提交新配置、审计并发布到配置 Store监听地址、存储连接和
已构造的安全/提取策略尚未自动重建,这些字段变更后需要重启 Controller。
## 2. 根结构
```yaml
version: 1
defaults: {}
security: {}
gateway: {}
distribution: {}
admin: {}
controlPlane: {}
metrics: {}
storage: {}
routing: []
upstreams: {}
```
- `defaults`Fetch 与 Check 的公共建议值。生产配置仍建议在每个启用的
Upstream 显式写出关键限制,避免继承关系不清。
- `security`:跨入口启动保护。
- `gateway`HTTP/HTTPS CONNECT 数据面入口。
- `distribution`:一次性独占提取入口。
- `admin`:运维管理入口,必须与 Distribution 分端口。
- `controlPlane`Worker 注册、Snapshot ACK 和 Runtime 心跳的 gRPC 控制面,默认关闭。
- `metrics`Prometheus 入口。
- `storage`Controller 使用的 PostgreSQL 与 Redis 地址。
- `routing`:有序 Routing 列表,自上而下首条命中停止。
- `upstreams`:全局共享的 Upstream 运行时定义。
## 3. 安全与监听器
```yaml
security:
requireProtectionOnPublicListen: true
```
启用严格保护时,只要 Gateway、Distribution 或 Admin 满足以下全部条件,
启动即失败:
1. 入口已启用且监听地址不是回环地址。
2. `auth.mode: none`
3. `access.allowCIDRs` 为空。
监听器公共字段:
```yaml
enabled: true
listen: 0.0.0.0:8081
access:
allowCIDRs: [10.0.0.0/8]
trustedProxies: [10.10.0.10/32]
auth:
mode: apiKey
header: X-API-Key
token: "${DISTRIBUTION_API_KEY}"
limits:
maxConcurrentConnections: 100000
requestsPerMinute: 6000
requestsPerMinutePerClient: 600
```
`trustedProxies` 只决定何时接受 `Forwarded``X-Forwarded-For`,不能替代
`allowCIDRs`。来自非可信代理的转发头必须忽略。
`requestsPerMinute``requestsPerMinutePerClient` 为非负整数,最大值为
`2^53-1`Distribution 的非零额度由 Redis Lua 计数,因此配置校验统一限制在
Lua 可精确表示的整数范围内。
### 3.1 认证模式
- `none`:无身份认证,访问控制与限流仍生效。
- `usernamePassword`:使用 `username``password`
- `apiKey`:使用 `header``token`
- `bearer`:使用 `Authorization: Bearer TOKEN`Gateway 对应
`Proxy-Authorization: Bearer TOKEN`
- `ipWhitelist`:使用 `cidrs`
- `any``methods` 中任一方法成功即可Bearer 方法的 Secret 使用
`value`/`valueFile`。
Gateway、Distribution、Admin 与 Provider API 是独立认证边界。改变其中一套
不得连带改变其他入口。
## 4. Worker 控制面
控制面默认关闭;默认配置中的 `8443` 端口预留不表示服务已监听。控制面 Session、
Snapshot ACK 和 Runtime 报告是 Redis 的短效运行时状态,**不写入 PostgreSQL**。
仅本机开发可以使用回环明文:
```yaml
controlPlane:
enabled: true
listen: 127.0.0.1:8443
protocolVersion: 1
heartbeatInterval: 10s
sessionTTL: 30s
maxStaleAge: 10s
maxMessageBytes: 1048576
maxRuntimeCounters: 100000
maxConcurrentStreams: 128
tls:
mode: disabled
```
任何非回环监听地址都必须使用 mTLS
```yaml
controlPlane:
enabled: true
listen: 0.0.0.0:8443
protocolVersion: 1
heartbeatInterval: 10s
sessionTTL: 30s
maxStaleAge: 10s
maxMessageBytes: 1048576
maxRuntimeCounters: 100000
maxConcurrentStreams: 128
tls:
mode: mtls
certFile: /run/secrets/controller-cert.pem
keyFile: /run/secrets/controller-key.pem
clientCAFile: /run/secrets/worker-ca.pem
trustDomain: proxy.example
environment: production
gatewayTLS:
certFile: /run/secrets/gateway-cert.pem
keyFile: /run/secrets/gateway-key.pem
serverCAFile: /run/secrets/controller-ca.pem
checkerTLS:
certFile: /run/secrets/checker-cert.pem
keyFile: /run/secrets/checker-key.pem
serverCAFile: /run/secrets/controller-ca.pem
```
- `protocolVersion` 当前固定为 `1`
- `sessionTTL` 至少为 `3 * heartbeatInterval``maxStaleAge` 不得短于心跳间隔。
- `maxMessageBytes` 范围为 `1..67108864``maxRuntimeCounters` 范围为
`1..1000000``maxConcurrentStreams` 必须为正数。
- `tls.mode` 只能为 `disabled``mtls`。`disabled` 只允许回环监听;`mtls` 必须
同时配置证书、私钥、客户端 CA、全小写 DNS `trustDomain` 和单 URI 路径段
`environment`
- `gatewayTLS` 是 Gateway 的客户端证书、私钥和 Controller CA`tls` 的服务端
证书和 Worker CA 分离。三项可以同时省略(未运行 Gateway配置任一项时必须完整提供。
- `checkerTLS``gatewayTLS` 有相同字段和完整性校验,但必须使用独立的 Checker
证书。Controller 分别验证 `.../worker/<worker-id>`
`.../checker/<checker-id>` SPIFFE URI不能跨角色复用证书。
Gateway 连接 Controller 时使用独立启动参数而非 `controlPlane.listen`。至少设置
`PROXY_POOL_CONTROL_PLANE_ADDRESS`、`PROXY_POOL_CLUSTER_ID`、
`PROXY_POOL_WORKER_ID`、`PROXY_POOL_INSTANCE_ID` 和 `PROXY_POOL_ZONE`,详见
[控制面协议](../api/control-plane.md#9-gateway-启动参数)。基础 Compose/Kubernetes
模板保持 `controlPlane.enabled: false`,环境 Overlay 挂载 mTLS 证书并启用后才可启动
Gateway。
Checker 同样使用独立的可拨号地址:`proxy-checker` 的 `-control-plane`
`-checker-id`、`-instance-id` 和 `-max-in-flight` 可由对应的
`PROXY_POOL_*` 环境变量提供。mTLS 模式下该命令读取 `checkerTLS`,明文 fixture
模式只接受回环 Controller 地址。Checker 只从 gRPC 领取任务并批量上报事实,不读取
Redis/PostgreSQLController 在生产启动拓扑中装配 Redis 共享任务队列,当前调度
HTTP/HTTPS/SOCKS5 BASIC、EGRESS 和 TARGET 检查。调度监督器在每轮从已发布配置读取启用的
上游与 RoutingAdmin reload 发布后,上游/路由启停、有效 `check` 策略和目标列表会在下一轮生效,
新启用的上游无需重启 Controller。
`maxRuntimeCounters` 同时限制单个 Runtime 报告和单个 Outcome 批次的条目数。Gateway
在本地维护容量为 `65536` 的非阻塞 Outcome 队列,默认微批上限为 `512`,实际取二者中
较小值;该队列与其序列确认状态仅存在于 Gateway 进程内。Controller 的 Redis 状态只保存
当前 Worker session 的最后确认序列和摘要PostgreSQL 不保存 Outcome、代理明细或逐请求记录。
## 5. Gateway
```yaml
gateway:
enabled: true
listen: 127.0.0.1:8080
auth: {mode: none}
limits:
maxConcurrentConnections: 50000
retry:
maxAttempts: 2
retryMethods: [GET, HEAD]
destinationPolicy:
denyPrivateNetworks: true
denyLoopback: true
denyLinkLocal: true
denyCIDRs: [169.254.169.254/32]
allowedPorts: [80, 443]
```
- `retryMethods` 默认只应包含幂等方法。POST、PUT、PATCH、DELETE 不自动重试。
- CONNECT 向 Client 写出 `200 Connection Established` 后不透明重放。
- 目的地址策略必须在 DNS 解析前后都执行,防止 DNS Rebinding。
- 三个 `deny*` 字段省略时均按 `true` 处理;只有显式配置为 `false` 才放行
对应类别,部分配置不会改变其他类别的安全默认值。
- `allowedPorts` 省略时安全默认值为 `[80, 443]`;配置空列表不表示开放全部端口。
- 保留地址、CGNAT 与云元数据端点始终拒绝,不能通过私网/链路本地开关放行。
- `maxConcurrentConnections` 是入口准入上限,不是 Proxy 容量上限。
## 6. Distribution
```yaml
distribution:
enabled: true
listen: 127.0.0.1:8081
auth: {mode: none}
clientIdentification: {mode: sourceIP}
limits:
requestsPerMinute: 60
requestsPerMinutePerClient: 30
extraction:
fulfillment: partial
maxCountPerRequest: 20
minRemainingTTL: 30s
maxHealthCheckAge: 15s
reserveForGateway: 5
idempotencyTTL: 5m
```
Extraction 是固定的一次性独占行为,**没有** `mode`、`leaseDuration`、
`release``renew` 配置。Redis 在一个原子操作内选择候选、执行
`AVAILABLE -> EXTRACTED` 并短期保存幂等结果;成功后该 Proxy 不再由 Gateway
或 Distribution 在当前 TTL 生命周期中分配。
- `fulfillment``partial` 或 `allOrNothing`,默认语义为 `partial`
- `maxCountPerRequest`:单次请求硬上限。
- `minRemainingTTL`:剩余寿命低于此值时不参与提取。
- `maxHealthCheckAge`:最近检查早于此窗口时不参与提取。
- `reserveForGateway`:提取后必须留给 Gateway 的最低符合条件库存数量。
- `idempotencyTTL`Redis 幂等结果的最长保留时间;省略时为 5 分钟,实际
保留时间不会超过本次返回代理中最早的 `expiresAt`
`partial` 会原子提取实际可得数量;`allOrNothing` 数量不足时不改变任何候选,
一个也不提取。认证关闭时仍应使用 `sourceIP` 识别匿名 Client 并执行全局/
来源限流。
`clientIdentification.mode` 支持:
- `sourceIP`:使用可信代理链解析后的来源地址;省略配置时采用此模式。
- `authenticatedClient`:使用 Basic 用户名或 Token 的稳定不可逆摘要;要求
启用认证。
- `authenticatedClientOrSourceIP`:优先认证主体,无主体时回退来源地址。
## 7. Routing
```yaml
routing:
- name: api-post
enabled: true
purpose: gateway
match:
hostRegex: '^api\\.example\\.com$'
methods: [POST]
pathRegex: '^/v1/'
headers: {X-Tenant: premium}
upstreams: [provider-a, provider-b]
strategy:
type: sequential
switchAfterEmptyFetch: 5
endBehavior: stop
onUnavailable:
action: reject
waitTimeout: 0s
check:
targets: [https://api.example.com/health]
```
- Routing 列表有序,首条匹配后停止。
- `purpose``gateway``extract`
- `strategy.type` 支持 `sequential`、`random`、`roundRobin`、`weighted`、
`leastConnections`
- `weighted` 使用 `weights` 映射,键必须引用本 Routing 的 Upstream。
- `sequential` 至少引用两个 Upstream并设置大于零的
`switchAfterEmptyFetch``endBehavior` 省略时默认为 `stop`,也可显式设置
`loop``stayLast`
- `onUnavailable.action``reject`、`wait` 或 `direct`;默认建议 `reject`
- `check.targets` 是 Routing 级 HTTP/HTTPS 探测目标。每个 Routing 最多 16 个 URL且一个
启用 Upstream 被其引用的 TARGET Profile 总数最多 64 个。每个 Profile 的身份是
`(routing.name, target URL)`;失败只影响该 Profile不改变 Proxy 的全局健康状态。
Sequential 的空计数属于 Upstream当前索引属于 Routing。只有 Provider 响应
成功、模板成功且合法候选为零时才增加空计数。错误不改变空计数;重复候选
会重置空计数但增加独立 duplicate 指标。
## 8. Upstream
```yaml
upstreams:
provider-a:
enabled: true
exposure: [gateway, extract]
provider:
billingMode: fetch
protocols: [http, https]
api:
url: https://provider-a.example/proxies
method: GET
auth:
type: apiKey
location: header
name: X-Provider-Key
value: "${PROVIDER_API_KEY}"
headers: {}
query: {count: '100'}
body: {type: json, value: {}}
template: '{{.}}'
proxyAuth: {type: response}
pool: {maxSize: 1000, shrinkDelay: 30s}
capacity: {maxConcurrencyPerProxy: 10}
refill:
reconcileInterval: 1s
minimumAvailableSlots: 2000
targetAvailableSlots: 5000
lifecycle: {ttl: 2m, allocationSafetyMargin: 15s}
fetch:
estimatedIPsPerCall: 100
requestInterval: 1s
timeout: 3s
maxAttempts: 3
maxInFlight: 1
maxTotal: 100000
maxResponseBytes: 1048576
templateTimeout: 100ms
retry: {initial: 500ms, max: 30s, jitter: 20}
check:
interval: 30s
jitter: 20
maxInFlight: 100
timeout: 2s
maxAttempts: 2
maxConsecutiveFailures: 3
urls: [http://connect.rom.miui.com/generate_204]
```
### 8.1 Provider 与代理认证
- `api.auth` 用于系统访问 Provider API。
- `proxyAuth` 用于最终连接被获取的 Proxy。
- Provider `api.auth.type` 支持 `none`、`basic`、`bearer`、`apiKey`。
- `apiKey` 使用 `location: header|query``name`、`value`,程序负责 Header
设置或 Query URL 编码。
- `basic` 使用 `username`、`password``bearer` 使用 `token`
- `proxyAuth.type: response` 表示凭据来自 Provider 响应。
- `proxyAuth.type: static` 使用配置中的 `username`、`password`。
- `proxyAuth.type: ipWhitelist` 表示 Provider 按出口 IP 放行,不配置账号密码。
Provider API 认证**不使用**入口的 `auth.mode`,代理连接认证也不使用
`mode`。三者的字段和凭据不可互相回退:
```yaml
# 对外 Distribution/Gateway/Admin
auth:
mode: apiKey
header: X-API-Key
token: "${CLIENT_API_KEY}"
# 请求 Provider API
auth:
type: bearer
token: "${PROVIDER_API_TOKEN}"
# 连接 Provider 返回的 Proxy
proxyAuth:
type: static
username: "${PROVIDER_PROXY_USER}"
password: "${PROVIDER_PROXY_PASSWORD}"
```
### 8.2 Pool 与累计额度
- `pool.maxSize`:当前系统维护且尚未 EXTRACTED 的 Proxy 硬上限,包括
FETCHED、CHECKING、AVAILABLE、SUSPECT、DRAINING 和 pending expected。
- `fetch.maxTotal`:当前 Redis generation 内的累计获取停止阈值;`0` 表示不设置。
Redis 在调用前原子校验 `fetched total + pending expected + expected`,并在达到
阈值后停止发起新调用。
`fetch.maxTotal` 不得小于 `pool.maxSize`。提取一个 Proxy 会释放当前库存位置,
但不会恢复累计获取额度。Provider 调用结果不确定、响应无法解析或 Permit 过期时,
系统按 `estimatedIPsPerCall` 保守记账避免故障或换主造成额度低估。Redis 全量
状态丢失会创建新 generation因此需要由外部计费系统提供跨 generation 的长期额度。
`estimatedIPsPerCall` 是预留估值,不是通用的 Provider 响应硬限制。如果某次实际
合法返回量超过估值,系统只保留本地池容量允许的数量,但累计账本按实际合法数量
记账并停止后续调用;该次可能越过停止阈值。需要绝对硬上限时,必须同时在 Provider
请求参数中配置供应商支持的批量上限,并保证其不超过剩余额度。
当前实现限制单个 `pool.maxSize <= 1,000,000`、配置内 Upstream 总数不超过
`4,096`并要求单代理并发、Refill 双水位、理论总槽位及其他传入 Redis Lua 的
累计/并发计数不超过 `2^53-1`。这些边界在配置加载和 Admin reload 提交前校验,
不会等到 Provider Runtime 启动后才失败。
`proxyAuth.type: response` 的用户名/密码是 Parser 到 Redis Activity Adapter 之间的
临时凭据。每次 Parser handoff 使用独立、幂等释放的 lease避免并发 Fetch 互相
删除或轮换版本覆盖;成功复制到 TTL 活动记录、候选被截断或解析失败后都会释放。
内存 lease 上限按所有配置 Upstream 的 `pool.maxSize * fetch.maxInFlight` 汇总,
配置 reload 只提高上限,不预分配对应内存。
### 8.3 Fetch 限制
- `estimatedIPsPerCall`:冷启动时每次 Provider 调用预计返回的合法 Proxy 数,
同时用于 `pool.maxSize` 的 pending 预占;不得从任意 Query 或 Body 字段推断。
- `requestInterval`:同一 Provider 请求间隔。
- `timeout`:单次调用超时。
- `maxAttempts`:单次补池动作最大尝试次数。
- `maxInFlight`:同一 Provider 同时在途请求数。
- `maxResponseBytes`:读取响应的硬上限。
- `templateTimeout`:模板解析执行上限。
- `retry`错误退避HTTP 429 还必须尊重 `Retry-After`
大量缺池信号必须合并成 singleflight 或容量为 1 的通知,不能按 Gateway 请求
数量线性触发 Provider API。
### 8.4 Refill 水位
- `reconcileInterval`:无事件时重新读取库存的兜底周期,启动时仍立即检查一次。
- `minimumAvailableSlots`:可用并发槽位低于该值时进入补池。
- `targetAvailableSlots`:进入补池后持续补到该目标,再退出补池状态。
三个字段均为必填正值,且 `targetAvailableSlots > minimumAvailableSlots`
目标不得超过 `pool.maxSize * capacity.maxConcurrencyPerProxy` 的理论上限。
`requestInterval` 只限制外部 API 调用不能兼任库存复核周期。pending Proxy
`estimatedIPsPerCall * maxConcurrencyPerProxy` 折算槽位,避免并发补池超量。
### 8.5 生命周期与健康
- 明确绝对过期时间优先于响应 TTL响应 TTL 优先于配置 `lifecycle.ttl`
- 距离过期不足 `allocationSafetyMargin` 时停止新分配。
- `check.jitter` 为调度抖动百分比,避免所有 Proxy 同时探测。
- `check.urls` 最多 16 个规范化 HTTP/HTTPS URL作为 EGRESS 检查的有界目标集;
BASIC 检查不依赖该字段。EGRESS 成功响应必须包含合法 IP支持纯文本 IP或 JSON 的
`ip`、`ip_address`、`address`、`origin`、`query` 字段。
- 启用 Routing 的 `check.targets` 会为其引用的每个启用 Upstream 创建 TARGET 检查组。BASIC、
EGRESS 与 TARGET 使用固定批次和轮转顺序,并共享该 Upstream 的 `check.maxInFlight`,避免
配置多个目标后产生无界检查流量。
- 第一次有意义失败进入 SUSPECT达到 `maxConsecutiveFailures` 后才进入
UNHEALTHY。
## 9. 存储、Admin 与 Metrics
```yaml
admin:
enabled: true
listen: 127.0.0.1:8082
auth: {mode: none}
metrics:
enabled: true
listen: 127.0.0.1:9090
storage:
postgresURL: "${POSTGRES_URL}"
redisURL: "${REDIS_URL}"
```
Proxy 明细不写入 PostgreSQL。Redis 是短效 Proxy 活动池的运行时事实源,使用
TTL 保存 Proxy 状态、所有权和过期时间,并原子执行独占提取及短期幂等结果
读写Redis 丢失后由 Provider 重新补池,不从 PostgreSQL 恢复原 Proxy。
PostgreSQL 只保存配置版本、Upstream/Routing 管理状态、Admin 审计与 Outbox
以及可选的无 Proxy 明细聚合指标。Distribution 提取不依赖 PostgreSQL因而
PostgreSQL 故障本身不应使 Redis 中可完成的 Extract 返回 `503`。Metrics 标签
禁止 Proxy IP、Client ID、Session、完整 URL 和 Request ID。
启用 Admin 的多 Controller 部署必须让所有副本读取同一版本化配置源、Secret
版本和 `PROXY_POOL_CONFIG_FINGERPRINT_KEY`。Supervisor 每秒比较本地完整配置的
HMAC-SHA-256 与 PostgreSQL 权威值:管理状态暂时不可读时沿用 last-known
Runtime指纹已更新但本地源尚未同步时停止旧 Provider待源匹配并通过预检后
按全局 revision 恢复,避免旧凭据换主或迟到旧配置回写。
Metrics 启用时 `listen` 必须是合法 `host:port`。该入口固定提供 `/livez`
`/readyz``/metrics`,不复用 Distribution/Admin 的认证边界;外部访问必须由
网络策略限制。当前 `/metrics` 已包含 Go/进程基础指标Provider、提取和容量等
业务指标仍在后续实施范围。Distribution 启用时 `/readyz` 只以 Redis 活动池为
服务流量门槛PostgreSQL 故障由 Admin 接口独立报告。Metrics 开关或监听地址
变更需要重启 Controller。
## 10. 启动前校验清单
1. `version` 必须为 `1`,未知字段拒绝。
2. 所有启用监听器具有合法 `host:port`
3. 非回环监听器满足认证或来源 CIDR 保护。
4. Routing 名称唯一,正则可编译,引用的 Upstream 存在。
5. Sequential 至少引用两个 Upstream、阈值大于零`onUnavailable.action` 明确。
6. 启用的 Upstream 有正数 `pool.maxSize`、并发、Refill 水位和 Fetch 估值/限制。
7. `allocationSafetyMargin < ttl`
8. `fetch.maxTotal == 0``fetch.maxTotal >= pool.maxSize`
9. `minimumAvailableSlots < targetAvailableSlots`,且目标不超过理论并发容量。
10. Distribution 的 fulfillment 合法,单次数量大于零。
11. Secret 未写入日志可见配置转储。