372 lines
13 KiB
Markdown
372 lines
13 KiB
Markdown
# 配置参考
|
||
|
||
## 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`。该入口已装配
|
||
PostgreSQL 管理面迁移、Redis 活动池、Distribution/Admin 独立监听与优雅停机;
|
||
Provider 自动补池、Metrics 探针和完整部署拓扑仍在后续实施范围。
|
||
|
||
所有时间值使用 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: {}
|
||
metrics: {}
|
||
storage: {}
|
||
routing: []
|
||
upstreams: {}
|
||
```
|
||
|
||
- `defaults`:Fetch 与 Check 的公共建议值。生产配置仍建议在每个启用的
|
||
Upstream 显式写出关键限制,避免继承关系不清。
|
||
- `security`:跨入口启动保护。
|
||
- `gateway`:HTTP/HTTPS CONNECT 数据面入口。
|
||
- `distribution`:一次性独占提取入口。
|
||
- `admin`:运维管理入口,必须与 Distribution 分端口。
|
||
- `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`。来自非可信代理的转发头必须忽略。
|
||
|
||
### 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. 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 容量上限。
|
||
|
||
## 5. 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`:优先认证主体,无主体时回退来源地址。
|
||
|
||
## 6. 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
|
||
```
|
||
|
||
- 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`。
|
||
|
||
Sequential 的空计数属于 Upstream,当前索引属于 Routing。只有 Provider 响应
|
||
成功、模板成功且合法候选为零时才增加空计数。错误不改变空计数;重复候选
|
||
会重置空计数但增加独立 duplicate 指标。
|
||
|
||
## 7. 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}
|
||
lifecycle: {ttl: 2m, allocationSafetyMargin: 15s}
|
||
fetch:
|
||
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]
|
||
```
|
||
|
||
### 7.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}"
|
||
```
|
||
|
||
### 7.2 Pool 与累计额度
|
||
|
||
- `pool.maxSize`:当前系统维护且尚未 EXTRACTED 的 Proxy 硬上限,包括
|
||
FETCHED、CHECKING、AVAILABLE、SUSPECT、DRAINING 和 pending expected。
|
||
- `fetch.maxTotal`:当前运行或计费周期内,从 Provider 成功获取的累计上限;
|
||
`0` 表示不设置累计上限。
|
||
|
||
`fetch.maxTotal` 不得小于 `pool.maxSize`。提取一个 Proxy 会释放当前库存位置,
|
||
但不会恢复累计获取额度。
|
||
|
||
### 7.3 Fetch 限制
|
||
|
||
- `requestInterval`:同一 Provider 请求间隔。
|
||
- `timeout`:单次调用超时。
|
||
- `maxAttempts`:单次补池动作最大尝试次数。
|
||
- `maxInFlight`:同一 Provider 同时在途请求数。
|
||
- `maxResponseBytes`:读取响应的硬上限。
|
||
- `templateTimeout`:模板解析执行上限。
|
||
- `retry`:错误退避;HTTP 429 还必须尊重 `Retry-After`。
|
||
|
||
大量缺池信号必须合并成 singleflight 或容量为 1 的通知,不能按 Gateway 请求
|
||
数量线性触发 Provider API。
|
||
|
||
### 7.4 生命周期与健康
|
||
|
||
- 明确绝对过期时间优先于响应 TTL,响应 TTL 优先于配置 `lifecycle.ttl`。
|
||
- 距离过期不足 `allocationSafetyMargin` 时停止新分配。
|
||
- `check.jitter` 为调度抖动百分比,避免所有 Proxy 同时探测。
|
||
- 第一次有意义失败进入 SUSPECT;达到 `maxConsecutiveFailures` 后才进入
|
||
UNHEALTHY。
|
||
|
||
## 8. 存储、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。
|
||
|
||
## 9. 启动前校验清单
|
||
|
||
1. `version` 必须为 `1`,未知字段拒绝。
|
||
2. 所有启用监听器具有合法 `host:port`。
|
||
3. 非回环监听器满足认证或来源 CIDR 保护。
|
||
4. Routing 名称唯一,正则可编译,引用的 Upstream 存在。
|
||
5. Sequential 至少引用两个 Upstream、阈值大于零,`onUnavailable.action` 明确。
|
||
6. 启用的 Upstream 有正数 `pool.maxSize`、并发和 Fetch 限制。
|
||
7. `allocationSafetyMargin < ttl`。
|
||
8. `fetch.maxTotal == 0` 或 `fetch.maxTotal >= pool.maxSize`。
|
||
9. Distribution 的 fulfillment 合法,单次数量大于零。
|
||
10. Secret 未写入日志可见配置转储。
|