proxy-pool/docs/configuration/reference.md

32 KiB
Raw Blame History

配置参考

1. 加载规则

主配置格式为 YAML根字段 version 当前固定为 1。加载器启用严格字段 检查,拼写错误或未来版本字段不会被静默忽略。当前仓库可使用与未来进程启动 相同的严格加载器校验本地部署配置:

go run ./deploy/tools/configcheck deploy/config/local.yaml

Controller 入口已实现,源码运行方式为:

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例如 500ms30s5m。示例中的 ${TOKEN}${PASSWORD}${POSTGRES_URL} 等由加载器从同名环境变量 展开;这些值不得写入日志、指标、配置转储或错误响应。生产配置优先使用 环境变量或 Secret 文件,不提交明文值。

加载与热更新必须遵循同一顺序:

读取文件 -> 严格解析 -> 字段校验 -> 引用/正则校验
        -> 构建不可变快照 -> 计算差异 -> 原子替换

新配置任何一步失败时保留旧快照。删除或禁用 Upstream 只停止新 Fetch 和新 分配,已有连接进入 Drain不强制中断。

当前 Controller 启动时使用同一份不可变快照完成存储、入口和提取策略装配。 Admin 重载会原子提交新配置、审计并发布到配置 Store监听地址、存储连接和 已构造的安全/提取策略尚未自动重建,这些字段变更后需要重启 Controller。

2. 根结构

version: 1
defaults: {}
security: {}
gateway: {}
distribution: {}
admin: {}
controlPlane: {}
metrics: {}
storage: {}
routing: []
upstreams: {}
  • defaultsFetch 与 Check 的公共建议值。生产配置仍建议在每个启用的 Upstream 显式写出关键限制,避免继承关系不清。
  • security:跨入口启动保护。
  • gatewayHTTP/HTTPS CONNECT 数据面入口。
  • distribution:一次性独占提取入口。
  • admin:运维管理入口,必须与 Distribution 分端口。
  • controlPlaneWorker 注册、Snapshot ACK 和 Runtime 心跳的 gRPC 控制面,默认关闭。
  • metricsPrometheus 入口。
  • storageController 使用的 PostgreSQL 与 Redis 地址。
  • routing:有序 Routing 列表,自上而下首条命中停止。
  • upstreams:全局共享的 Upstream 运行时定义。

3. 安全与监听器

security:
  requireProtectionOnPublicListen: true

启用严格保护时,只要 Gateway、Distribution 或 Admin 满足以下全部条件, 启动即失败:

  1. 入口已启用且监听地址不是回环地址。
  2. auth.mode: none
  3. access.allowCIDRs 为空。

监听器公共字段:

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 只决定何时接受 ForwardedX-Forwarded-For,不能替代 allowCIDRs。来自非可信代理的转发头必须忽略。

requestsPerMinuterequestsPerMinutePerClient 为非负整数,最大值为 2^53-1Distribution 的非零额度由 Redis Lua 计数,因此配置校验统一限制在 Lua 可精确表示的整数范围内。

3.1 认证模式

  • none:无身份认证,访问控制与限流仍生效。
  • usernamePassword:使用 usernamepassword
  • apiKey:使用 headertoken
  • bearer:使用 Authorization: Bearer TOKENGateway 对应 Proxy-Authorization: Bearer TOKEN
  • ipWhitelist:使用 cidrs
  • anymethods 中任一方法成功即可Bearer 方法的 Secret 使用 value/valueFile

3.2 端点权限

auth.permissions 是普通认证方式Basic、API Key、Bearer、IP 白名单)的固定权限 集合。未配置时保留历史全权限行为;显式配置后只允许下列有限值:

  • admin:read:读取 Admin Status 与审计页。
  • admin:write:执行 Upstream/Routing/配置变更。
  • distribution:extract:调用 Distribution 独占提取。
  • *:所有权限,且必须单独出现。

mode: any 必须将 permissions 写在各 methods 项中,权限随实际命中的凭据 返回,顶层 auth.permissions 不允许同时配置。权限值不接受空白、重复或未知项。 缺少所需权限的已认证请求返回 403,并在进入 JSON 解码、提取或管理 mutation 前 结束。mode: none 不允许配置权限,避免把匿名访问误配置为受控授权。

admin:
  auth:
    mode: any
    methods:
      - mode: bearer
        valueFile: /run/secrets/admin-reader-token
        permissions: [admin:read]
      - mode: bearer
        valueFile: /run/secrets/admin-writer-token
        permissions: [admin:read, admin:write]

3.3 Distribution Client 提取约束

认证凭据可在 auth.client 中设置固定的 Client 约束mode: any 则必须在实际 命中的 auth.methods 项下配置。当前 Client 约束按入口明确分工,避免出现只声明 不执行的访问控制。

  • maxExtractCountDistribution 单次提取的额外上限0 表示不追加上限。
  • allowedUpstreams允许访问的 Upstream 名称集合。省略请求过滤条件时,服务端 自动使用这个集合;请求携带集合外名称会返回 403。
  • allowedRegions允许访问的地区集合行为与 allowedUpstreams 相同。

空 client 配置保留旧版兼容语义不会限制已认证凭据。Upstream 名称必须引用 当前配置中已有的 Upstream。

distribution:
  auth:
    mode: any
    methods:
      - mode: apiKey
        header: X-API-Key
        valueFile: /run/secrets/tenant-a-key
        permissions: [distribution:extract]
        client:
          maxExtractCount: 20
          allowedUpstreams: [provider-a, provider-b]
          allowedRegions: [shanghai, beijing]

3.4 Gateway Client 路由约束

Gateway 的 auth.client 支持 allowedRoutings、requestsPerMinute 和 maxConcurrentConnections。认证成功后 Gateway 先完成目标地址策略和本地 Routing 匹配,再于本地 Dispatcher 前检查 Routing 集合;不匹配返回 403不会尝试选择或预留 Proxy。速率和并发限制均以 认证 Client 为计数键,在每个 Gateway Worker 内执行,超过限制返回 429。三项 检查都只读取请求上下文和当前本地状态,不访问 Redis、PostgreSQL 或 Provider。

allowedRoutings 中的每个名称必须对应一个启用的 Gateway Routing。Gateway 上的 maxExtractCount、allowedUpstreams 和 allowedRegions以及 Distribution 上的 allowedRoutings 和 requestsPerMinute 均会被配置校验拒绝。Admin 不支持任何 client 约束。

gateway:
  auth:
    mode: any
    methods:
      - mode: bearer
        valueFile: /run/secrets/checkout-gateway-token
        client:
          allowedRoutings: [checkout]
          requestsPerMinute: 600
          maxConcurrentConnections: 20

Gateway、Distribution、Admin 与 Provider API 是独立认证边界。改变其中一套 不得连带改变其他入口。

3.5 Gateway 粘性会话

gateway.stickySession 让调用方使用一个会话头尽量固定出口 Proxy。绑定键是认证 Client、已匹配 Routing 和会话标识的组合,因此不同 Client 或不同 Routing 使用 相同会话值也不会共享出口。该功能要求 Gateway 认证开启。

gateway:
  auth:
    mode: bearer
    tokenFile: /run/secrets/gateway-token
  stickySession:
    enabled: true
    header: X-Proxy-Session
    ttl: 20s
    maxEntries: 100000
  • header 必须是规范 HTTP Header 名;一个请求只能携带一个值,值由字母、数字、 点、下划线、连字符组成且最长 128 字节。
  • ttl 是绑定的最长时长;实际到期时间还会被 Proxy 的 usableUntil、代理过期时间 和 allocation safety margin 截短。
  • maxEntries 是当前 Gateway Worker 的硬上限。缓存只保存 Proxy ID 和过期时间, 不保存 Proxy 地址、凭据或原始会话标识。
  • 会话头仅用于本地选路Gateway 在转发 HTTP 请求前删除它。Proxy 不再符合当前 Snapshot 或一次代理尝试失败时,绑定会被清除并按正常 Routing 重选。
  • 该版本不把 Redis、PostgreSQL 或 gRPC 引入 Gateway 热路径;跨 Worker 粘性需要 上游负载均衡保持 Worker 亲和,后续由控制面快照协议扩展共享恢复能力。

4. Worker 控制面

控制面默认关闭;默认配置中的 8443 端口预留不表示服务已监听。控制面 Session、 Snapshot ACK 和 Runtime 报告是 Redis 的短效运行时状态,不写入 PostgreSQL

仅本机开发可以使用回环明文:

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

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 * heartbeatIntervalmaxStaleAge 不得短于心跳间隔。
  • maxMessageBytes 范围为 1..67108864maxRuntimeCounters 范围为 1..1000000maxConcurrentStreams 必须为正数。
  • tls.mode 只能为 disabledmtlsdisabled 只允许回环监听;mtls 必须 同时配置证书、私钥、客户端 CA、全小写 DNS trustDomain 和单 URI 路径段 environment
  • gatewayTLS 是 Gateway 的客户端证书、私钥和 Controller CAtls 的服务端 证书和 Worker CA 分离。三项可以同时省略(未运行 Gateway配置任一项时必须完整提供。
  • checkerTLSgatewayTLS 有相同字段和完整性校验,但必须使用独立的 Checker 证书。Controller 分别验证 .../worker/<worker-id>.../checker/<checker-id> SPIFFE URI不能跨角色复用证书。

Gateway 连接 Controller 时使用独立启动参数而非 controlPlane.listen。至少设置 PROXY_POOL_CONTROL_PLANE_ADDRESSPROXY_POOL_CLUSTER_IDPROXY_POOL_WORKER_IDPROXY_POOL_INSTANCE_IDPROXY_POOL_ZONE,详见 控制面协议。Compose 本地模板使用固定 Worker/Checker 身份和被忽略的开发证书目录Kubernetes Base 保持 controlPlane.enabled: false,必须由具备每工作负载唯一身份的 mTLS Overlay 启用。

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

gateway:
  enabled: true
  listen: 127.0.0.1:8080
  auth: {mode: none}
  limits:
    maxConcurrentConnections: 50000
  transport:
    dialTimeout: 10s
    handshakeTimeout: 15s
    responseHeaderTimeout: 30s
    idleConnTimeout: 90s
    maxIdleConns: 20000
    maxIdleConnsPerHost: 32
    maxConnsPerHost: 32
    tunnelBufferBytes: 32768
    tunnelIdleTimeout: 5m
  retry:
    maxAttempts: 2
    retryMethods: [GET, HEAD]
  stickySession:
    enabled: true
    header: X-Proxy-Session
    ttl: 20s
    maxEntries: 100000
  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.1 Gateway 本地传输层

gateway.transport 只配置每个 Gateway Worker 的本地 HTTP Transport 和 CONNECT 隧道 I/O不读取或写入 Redis、PostgreSQL、Provider也不参与热路径分配。修改 这些字段需要滚动重启对应 Gateway Worker 后生效。

  • dialTimeouthandshakeTimeoutresponseHeaderTimeoutidleConnTimeout:分别限制拨号、上游 TLS/CONNECT 握手、HTTP 响应头和空闲 HTTP 连接的时长。
  • maxIdleConns:单个本地 HTTP Transport 的总空闲连接上限。Gateway 分别为代理 转发和 direct fallback 建立 Transport最坏情况下两者都可能保有空闲连接。
  • maxIdleConnsPerHost:单个上游 Host 的空闲 HTTP 连接上限。
  • maxConnsPerHost:单个上游 Host 的 HTTP 活跃加空闲连接上限。它不替代 limits.maxConcurrentConnections,也不限制已经建立的 CONNECT 隧道。
  • tunnelBufferBytes:每个活跃 CONNECT 隧道的每个复制方向使用一个缓冲区,内存 预算至少按 活跃隧道数 * 2 * tunnelBufferBytes 计算。
  • tunnelIdleTimeoutCONNECT 隧道双向没有数据活动时的最长存活时间。

所有字段均为可选项;数值 0 表示沿用进程默认值:拨号 10s、握手 15s、 响应头 30s、空闲 HTTP 连接 90s、总空闲连接 1024、每 Host 空闲连接 64、隧道缓冲 32768 字节、隧道空闲 5m。计数和时长不得为负数 maxIdleConnsPerHost 不得大于显式配置的 maxIdleConns。示例中的 20000/32 是高并发 HTTP 转发的起点,不是每秒请求量的换算公式,应同时按可用文件描述符、 上游限额和观测到的复用率调节。

6. Distribution

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 是固定的一次性独占行为,没有 modeleaseDurationreleaserenew 配置。Redis 在一个原子操作内选择候选、执行 AVAILABLE -> EXTRACTED 并短期保存幂等结果;成功后该 Proxy 不再由 Gateway 或 Distribution 在当前 TTL 生命周期中分配。

  • fulfillmentpartialallOrNothing,默认语义为 partial
  • maxCountPerRequest:单次请求硬上限。
  • minRemainingTTL:剩余寿命低于此值时不参与提取。
  • maxHealthCheckAge:最近检查早于此窗口时不参与提取。
  • reserveForGateway:提取后必须留给 Gateway 的最低符合条件库存数量。
  • idempotencyTTLRedis 幂等结果的最长保留时间;省略时为 5 分钟,实际 保留时间不会超过本次返回代理中最早的 expiresAt

partial 会原子提取实际可得数量;allOrNothing 数量不足时不改变任何候选, 一个也不提取。认证关闭时仍应使用 sourceIP 识别匿名 Client 并执行全局/ 来源限流。

clientIdentification.mode 支持:

  • sourceIP:使用可信代理链解析后的来源地址;省略配置时采用此模式。
  • authenticatedClient:使用 Basic 用户名或 Token 的稳定不可逆摘要;要求 启用认证。
  • authenticatedClientOrSourceIP:优先认证主体,无主体时回退来源地址。

7. Routing

routing:
  - name: api-post
    enabled: true
    purpose: gateway
    action: proxy
    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 列表有序,首条匹配后停止。
  • purposegatewayextract
  • action 省略时为 proxyproxy 使用 Upstream 与 Strategydirect 直接连接已通过 目标地址策略验证的目标;reject 拒绝匹配请求。directreject 仅允许 gateway Routing且不得同时配置 upstreamsstrategyonUnavailablecheck.targets
  • strategy.type 支持 sequentialrandomroundRobinweightedleastConnections
  • weighted 使用 weights 映射,键必须引用本 Routing 的 Upstream。
  • sequential 至少引用两个 Upstream并设置大于零的 switchAfterEmptyFetchendBehavior 省略时默认为 stop,也可显式设置 loopstayLast
  • onUnavailable.actionrejectwaitdirect;默认建议 reject
  • action: direct 在 Dispatcher 前执行,不申请 Proxy 容量、不写 Proxy Outcome也不建立 粘性 Proxy 绑定它仍执行入口认证、Client 路由约束、速率/并发限制和目标地址策略。 onUnavailable.action: direct 仅是 action: proxy 在本地没有候选时的回退,二者 不可互换。
  • check.targets 是 Routing 级 HTTP/HTTPS 探测目标。每个 Routing 最多 16 个 URL且一个 启用 Upstream 被其引用的 TARGET Profile 总数最多 64 个。每个 Profile 的身份是 (routing.name, target URL);失败只影响该 Profile不改变 Proxy 的全局健康状态。

Sequential 的空计数属于 Upstream当前索引属于 Routing。只有 Provider 响应 成功、模板成功且合法候选为零时才增加空计数。错误不改变空计数;重复候选 会重置空计数但增加独立 duplicate 指标。

Controller 在连续空结果达到阈值时按顺序切换当前 Upstream到达最后一个可用 Upstream 且 endBehavior: stop 时,会以当前 Upstream 的 CAS 条件原子停用该 Routing。停用后的 Gateway 规则按既有 onUnavailable.action 执行 rejectwaitdirect,直到一次配置重载提交新的管理快照。

8. Upstream

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
      unhealthyRemoveAfter: 0s
      urls: [http://connect.rom.miui.com/generate_204]

8.1 Provider 与代理认证

  • api.auth 用于系统访问 Provider API。
  • proxyAuth 用于最终连接被获取的 Proxy。
  • Provider api.auth.type 支持 nonebasicbearerapiKey
  • apiKey 使用 location: header|querynamevalue,程序负责 Header 设置或 Query URL 编码。
  • basic 使用 usernamepasswordbearer 使用 token
  • proxyAuth.type: response 表示凭据来自 Provider 响应。
  • proxyAuth.type: static 使用配置中的 usernamepassword
  • proxyAuth.type: ipWhitelist 表示 Provider 按出口 IP 放行,不配置账号密码。

Provider API 认证不使用入口的 auth.mode,代理连接认证也不使用 mode。三者的字段和凭据不可互相回退:

# 对外 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 的 ipip_addressaddressoriginquery 字段。
  • 启用 Routing 的 check.targets 会为其引用的每个启用 Upstream 创建 TARGET 检查组。BASIC、 EGRESS 与 TARGET 使用固定批次和轮转顺序,并共享该 Upstream 的 check.maxInFlight,避免 配置多个目标后产生无界检查流量。
  • 第一次有意义失败进入 SUSPECT达到 maxConsecutiveFailures 后才进入 UNHEALTHY。
  • unhealthyRemoveAfter 控制 UNHEALTHY 持续多久后可由 Controller 回收;0s (默认)表示只保留异常状态,永不因该策略删除。首次进入 UNHEALTHY 的时间会跨 CHECKING 重试保持不变,成功恢复 AVAILABLE 时清除。
  • 回收器只运行在 Controller 后台,使用 Redis 有序索引和固定批次,不进入 Gateway 请求热路径。没有 Worker ownership 的 Proxy 在同一清理边界删除;仍属于 Worker 的候选 先在有界回执中携带 worker、assignment epoch 和首次异常时间,再由条件式 Drain 原子 复核 UNHEALTHY 状态与归属未变化后创建 Ticket。后续必须等待排除快照 ACK 与 Runtime 的 Active/Reserved 归零,才会释放 ownership 并由下一轮回收,避免删除仍可能出现在 Gateway Snapshot 中的代理。

9. 存储、Admin 与 Metrics

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/进程基础指标,以及 Checker 的 proxy_pool_checker_tasks_dispatched_total{level}proxy_pool_checker_observations_total{level,result}level 固定为 BASIC、EGRESS、TARGET result 固定为 accepted、rejected。Gateway 另暴露 proxy_pool_gateway_outcomes_total{stage,result}proxy_pool_gateway_outcome_queue_dropped_totalstage 固定为 DIAL、PROXY_HANDSHAKE、 RESPONSE_HEADERS、TUNNELresult 固定为 success、failure。Provider、Extraction、 Capacity 和 Drain 业务指标也已接入,所有标签均为固定枚举;绝不包含 Proxy、IP、Checker、 Client、路由、目标 URL 或凭据标签。Capacity 从既有 Provider 库存对账循环聚合, 不进入 Gateway 热路径。Distribution 启用时 /readyz 只以 Redis 活动池为 服务流量门槛PostgreSQL 故障由 Admin 接口独立报告。Metrics 开关或监听地址 变更需要重启 Controller。Controller、Gateway 和 Checker 的进程级致命错误以 JSON 结构化日志输出包含组件和稳定错误类型不输出错误原文敏感属性、URL 用户信息和 查询 Secret 在写出前统一替换为 [REDACTED]

Gateway 还按每个 Worker 暴露 HTTP/CONNECT 请求总数、在途请求数和活跃 CONNECT 隧道数;协议标签固定为 HTTP、CONNECT。请求数在 Handler 接受请求时增加,在途数在 全部拒绝、转发或隧道关闭后归零;活跃隧道只覆盖成功建立并开始 relay 的连接。这些 指标是连接池、入口并发、文件描述符和长连接排空的本地观测,不携带 Proxy、路由、 目标、Client 或凭据。

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 == 0fetch.maxTotal >= pool.maxSize
  9. minimumAvailableSlots < targetAvailableSlots,且目标不超过理论并发容量。
  10. Distribution 的 fulfillment 合法,单次数量大于零。
  11. Secret 未写入日志可见配置转储。