Reference

生产中的 CaptchaAI:配置管理指南

验证码识别任务突然大批量失败,打开代码一看:CAPTCHAAI_API_KEY 还写死在脚本里,是上周就该换掉的旧值。写死在代码里的配置,谁都不敢碰——原型阶段凑合,生产环境不行。要解决的其实就三件事:环境隔离、密钥不进库、改配置不用重新部署。

生产环境常见配置问题排查

常见的坑,先列全:

问题 原因 处理方式
API 密钥未加载 缺少环境变量,或变量名拼错 检查 echo $CAPTCHAAI_API_KEY;核对拼写
配置文件被忽略 路径错误,或缺少 YAML 库 确认文件存在;安装 pyyaml / js-yaml
生产环境用了开发配置 没有应用环境专属的覆盖值 检查环境变量优先级;确认 NODE_ENV / APP_ENV 设置正确
日志里能看到密钥 配置转储把 API 密钥一起打印出来 在日志输出前屏蔽敏感字段

密钥管理:别把 API Key 存进代码库

API 密钥不该出现在配置文件或源代码管理里。

方式 适用场景 示例
环境变量 容器、CI/CD export CAPTCHAAI_API_KEY=abc123
AWS Secrets Manager AWS 基础设施 启动时读取,支持自动轮换
HashiCorp Vault 多云、自建机房 带 TTL 的动态密钥
Docker Secrets Docker Swarm / Compose 挂载在 /run/secrets/
.env 文件(仅本地开发用) 本地开发 配合 dotenv,记得写进 .gitignore

完整配置参数速查表

参数 环境变量 默认值 说明
API 密钥 CAPTCHAAI_API_KEY 必需,你的 CaptchaAI API Key
提交地址 CAPTCHAAI_SUBMIT_URL https://ocr.captchaai.com/in.php 任务提交接口
轮询地址 CAPTCHAAI_POLL_URL https://ocr.captchaai.com/res.php 结果轮询接口
轮询间隔 CAPTCHAAI_POLL_INTERVAL 5 两次轮询间隔秒数
最大轮询次数 CAPTCHAAI_MAX_POLLS 60 超时前的最大轮询次数
并发数 CAPTCHAAI_CONCURRENCY 10 同时处理的验证码任务上限
超时 CAPTCHAAI_TIMEOUT 300 单个任务超时时间(秒)
代理 CAPTCHAAI_PROXY 识别使用的代理 URL
回调地址 CAPTCHAAI_CALLBACK_URL 接收异步结果的 Webhook URL
重试次数 CAPTCHAAI_RETRIES 3 临时失败时的重试次数
日志级别 CAPTCHAAI_LOG_LEVEL info 日志输出的详细程度

配置优先级:环境变量 > 配置文件 > 默认值

Priority (highest → lowest):

1. Environment variables     ← deployment-specific overrides
2. Config file (YAML/JSON)   ← version-controlled defaults
3. Application defaults      ← fallback values in code

环境变量优先级最高,配置文件其次,默认值只是兜底——离操作者越近的设置越说了算。

配置加载器怎么写:Python 与 JavaScript 示例

Python

import os
import yaml
from dataclasses import dataclass, field
from pathlib import Path


@dataclass
class CaptchaAIConfig:
    api_key: str = ""
    submit_url: str = "https://ocr.captchaai.com/in.php"
    poll_url: str = "https://ocr.captchaai.com/res.php"
    poll_interval: int = 5
    max_polls: int = 60
    concurrency: int = 10
    timeout: int = 300
    proxy: str = ""
    callback_url: str = ""
    retries: int = 3
    log_level: str = "info"

    @classmethod
    def load(cls, config_path=None):
        """Load config: env vars override file, which overrides defaults."""
        config = cls()

        # Layer 2: Config file
        if config_path and Path(config_path).exists():
            with open(config_path) as f:
                file_config = yaml.safe_load(f) or {}
            for key, value in file_config.items():
                if hasattr(config, key):
                    setattr(config, key, value)

        # Layer 1: Environment variables (highest priority)
        env_map = {
            "CAPTCHAAI_API_KEY": "api_key",
            "CAPTCHAAI_SUBMIT_URL": "submit_url",
            "CAPTCHAAI_POLL_URL": "poll_url",
            "CAPTCHAAI_POLL_INTERVAL": "poll_interval",
            "CAPTCHAAI_MAX_POLLS": "max_polls",
            "CAPTCHAAI_CONCURRENCY": "concurrency",
            "CAPTCHAAI_TIMEOUT": "timeout",
            "CAPTCHAAI_PROXY": "proxy",
            "CAPTCHAAI_CALLBACK_URL": "callback_url",
            "CAPTCHAAI_RETRIES": "retries",
            "CAPTCHAAI_LOG_LEVEL": "log_level",
        }

        for env_key, attr_name in env_map.items():
            value = os.environ.get(env_key)
            if value is not None:
                # Cast to correct type
                current = getattr(config, attr_name)
                if isinstance(current, int):
                    value = int(value)
                setattr(config, attr_name, value)

        config.validate()
        return config

    def validate(self):
        if not self.api_key:
            raise ValueError("CAPTCHAAI_API_KEY is required")
        if self.poll_interval < 1:
            raise ValueError("poll_interval must be >= 1")
        if self.concurrency < 1:
            raise ValueError("concurrency must be >= 1")


# Usage
config = CaptchaAIConfig.load("config/captchaai.yaml")
print(f"Concurrency: {config.concurrency}, Timeout: {config.timeout}s")

JavaScript

const fs = require("fs");
const yaml = require("js-yaml");
const path = require("path");

class CaptchaAIConfig {
  static defaults = {
    apiKey: "",
    submitUrl: "https://ocr.captchaai.com/in.php",
    pollUrl: "https://ocr.captchaai.com/res.php",
    pollInterval: 5,
    maxPolls: 60,
    concurrency: 10,
    timeout: 300,
    proxy: "",
    callbackUrl: "",
    retries: 3,
    logLevel: "info",
  };

  static envMap = {
    CAPTCHAAI_API_KEY: "apiKey",
    CAPTCHAAI_SUBMIT_URL: "submitUrl",
    CAPTCHAAI_POLL_URL: "pollUrl",
    CAPTCHAAI_POLL_INTERVAL: { key: "pollInterval", type: "int" },
    CAPTCHAAI_MAX_POLLS: { key: "maxPolls", type: "int" },
    CAPTCHAAI_CONCURRENCY: { key: "concurrency", type: "int" },
    CAPTCHAAI_TIMEOUT: { key: "timeout", type: "int" },
    CAPTCHAAI_PROXY: "proxy",
    CAPTCHAAI_CALLBACK_URL: "callbackUrl",
    CAPTCHAAI_RETRIES: { key: "retries", type: "int" },
    CAPTCHAAI_LOG_LEVEL: "logLevel",
  };

  static load(configPath = null) {
    let config = { ...CaptchaAIConfig.defaults };

    // Layer 2: Config file
    if (configPath && fs.existsSync(configPath)) {
      const ext = path.extname(configPath);
      const raw = fs.readFileSync(configPath, "utf8");
      const fileConfig = ext === ".json" ? JSON.parse(raw) : yaml.load(raw);
      config = { ...config, ...fileConfig };
    }

    // Layer 1: Environment variables
    for (const [envKey, mapping] of Object.entries(CaptchaAIConfig.envMap)) {
      const value = process.env[envKey];
      if (value !== undefined) {
        const attrKey = typeof mapping === "string" ? mapping : mapping.key;
        const type = typeof mapping === "string" ? "string" : mapping.type;
        config[attrKey] = type === "int" ? parseInt(value, 10) : value;
      }
    }

    CaptchaAIConfig.validate(config);
    return config;
  }

  static validate(config) {
    if (!config.apiKey) throw new Error("CAPTCHAAI_API_KEY is required");
    if (config.pollInterval < 1) throw new Error("pollInterval must be >= 1");
    if (config.concurrency < 1) throw new Error("concurrency must be >= 1");
  }
}

// Usage
const config = CaptchaAIConfig.load("config/captchaai.yaml");
console.log(`Concurrency: ${config.concurrency}, Timeout: ${config.timeout}s`);

CI/CD 跑在国内网络时,给 pip install 加个国内镜像能明显缓解拉取超时:pip install pyyaml -i https://pypi.tuna.tsinghua.edu.cn/simple

按环境拆分配置文件:base / 生产 / 预发布

# config/captchaai.yaml — base
api_key: ""  # Always set via env var
concurrency: 5
poll_interval: 5
retries: 3
log_level: info
# config/captchaai.production.yaml
concurrency: 20
poll_interval: 3
timeout: 180
log_level: warning
# config/captchaai.staging.yaml
concurrency: 3
poll_interval: 5
timeout: 300
log_level: debug

base 放安全默认值,production / staging 只覆盖差异字段:生产并发更高,预发布放宽超时、开 debug 日志。

Docker Compose 部署示例

services:
  captcha-worker:
    image: captcha-worker:latest
    environment:

      - CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
      - CAPTCHAAI_CONCURRENCY=15
      - CAPTCHAAI_LOG_LEVEL=warning
    env_file:

      - .env.production

功能开关:不重新部署也能调整行为

class FeatureFlags:
    def __init__(self):
        self.flags = {
            "use_callback": os.environ.get("FF_USE_CALLBACK", "false") == "true",
            "enable_proxy": os.environ.get("FF_ENABLE_PROXY", "true") == "true",
            "max_concurrent": int(os.environ.get("FF_MAX_CONCURRENT", "10")),
        }

    def is_enabled(self, flag):
        return self.flags.get(flag, False)

    def get(self, flag, default=None):
        return self.flags.get(flag, default)

功能开关本质是环境变量的封装,不改代码就能切换行为,比如关掉回调或停用代理。

常见问题

环境变量和配置文件冲突,以哪个为准?

环境变量优先。配置文件写了 concurrency: 5,只要环境变量设了 CAPTCHAAI_CONCURRENCY=20,实际生效的就是 20。

怎么避免 API Key 被打印到日志里?

常见原因是配置对象被整体输出到日志。给日志组件加字段屏蔽,api_key 等敏感字段替换成 ***

多久轮换一次 CaptchaAI 的 API Key?

怀疑泄漏立刻轮换,常规场景按 90 天一轮,优先选支持自动轮换的方案。

相关文章

下一步

把配置方案落到生产环境——从这里拿 CaptchaAI API Key,照着上面的模板逐层搭起来。

相关指南:

该文章已禁用评论。