Cookie-aware proxy pool with CF challenge solving - Tiered proxy pool (fast 50 + main 676) - Per-proxy cf_clearance cookie persistence - CF challenge detection + user-browser solving - Safari + Chrome TLS fingerprint rotation - Async FastAPI backend with LRU cache - Passive daemon with systemd supervision - Stats dashboard + Prometheus metrics
8.1 KiB
8.1 KiB
AO3 Mirror — SOUL v4
项目宗旨
为中国用户恢复 archiveofourown.org 的访问。维护一个高可用、高效、经济的反向代理镜像站。 目标:单台服务器承受 200+ QPS,CF 5 秒盾自动求解。
核心指标
| 指标 | 当前值 | 目标 |
|---|---|---|
| 本地 QPS(缓存命中) | 720 QPS | ≥200 |
| 平均延迟(缓存命中) | 14-42ms | ≤100ms |
| 代理存活率(AO3/CF) | ~72% (最佳端口) | ≥70% |
| 代理池 | 726 WebShare (最佳端口) | — |
| 有效代理 | ~524 (72%) | ≥500 |
| Cookie 代理 | — | ≥200 (持有 cf_clearance) |
| CF 挑战 | 主动求解 (用户浏览器) | 零感知 |
架构总览(v4 — Cookie 感知 + CF 挑战求解)
用户 ──→ Cloudflare CDN ──→ Caddy (443, 60s) ──→ 2× uvicorn workers (8081-8082) [全异步]
│
┌──────┴──────┐
│ Tiered Pool │
│ │
│ Fast (50) │ ← POST/login (8s, 1 retry)
│ Main (676) │ ← GET/browse (15s, 2 retries)
│ │
│ Cookie 感知 │ ← 每个代理持有自己的 cookie jar
│ (cf_clearance│ 成功请求后自动保存 Set-Cookie
│ 等持久化) │ 后续请求自动注入
└──────┬──────┘
│
curl_cffi AsyncSession
(safari15_5/17_0, chrome123/124
TLS 指纹 + 连接复用)
v4 关键改进
| 改进 | v3 | v4 |
|---|---|---|
| Cookie 持久化 | ❌ 裸请求 | ✅ 每代理 cookie jar |
| CF 挑战检测 | ❌ 当代理死亡 | ✅ 分离检测,不退避 |
| CF 挑战求解 | ❌ 放弃 | ✅ 用户浏览器自动求解 |
| 代理亲和性 | ❌ 随机 | ✅ Challenge token 保证同 proxy |
| TLS 指纹 | chrome123/124 | safari15_5, safari17_0, chrome123/124 |
| 统计准确度 | ❌ alive=total (假数据) | ✅ 实时遍历计数 |
| Cookie 代理数 | N/A | ✅ 统计面板展示 |
| 用户感知 | 502 错误页 | 短暂「检查浏览器」→ 自动恢复 |
核心组件
1. 异步代理池 (proxy_pool.py) v4
ProxySession— 每个代理持有自己的AsyncSession+ cookie jar- Cookie 生命周期:成功请求自动保存 → 过期自动清理 → 后续请求自动注入
- CF 挑战分离:
mark_challenged()≠mark_failure()— 不退避 - 加权轮询:响应速度越快的代理,被选中的概率越大
- 指数退避:仅对真失败 (connection error/timeout) → 5s→15s→30s→60s→120s→300s
- Cookie-aware 选择:
get_proxy_with_cookies()优先返回持有 cf_clearance 的代理
2. 异步抓取器 (ao3_fetcher.py) v4
async fetch_url()— Cookie 感知 + CF 挑战检测 + 智能重试is_cf_challenge()— 检测 403/503 是否为 CF 挑战页(body markers + Server header)- 智能重试策略:
- 首次尝试 → 优先用带 cookie 的代理
- 遇到 CF 挑战 → 换代理(不退避)
- 遇到真失败 → 退避 + 换代理
- 重试 2 次(fast 路径 1 次)
- 请求超时:15s (普通), 8s (交互)
3. 后端服务 (app.py) v4
FastAPI,绑定 127.0.0.1。
- Challenge token 映射:
_challenge_map[token] → (method, url, headers, body, proxy_host)- 用户浏览器求解 CF 挑战时,保证后续请求使用同一代理(IP 亲和性)
- TTL 120s,过期自动清理
- 挑战页面透传:所有代理都遇 CF 挑战 → 重写页面 → 发给用户浏览器
- 注入
_cf_tokencookie 和 meta 标签 - 用户浏览器自动执行 CF JS → 验证通过 → redirect 回镜像
- Worker 捕获 cf_clearance → 保存到代理 cookie jar
- 注入
- Cookie 流转发:AO3 的 Set-Cookie (用户 session + cf_clearance) 域名重写后传给用户
- URL 重写:
archiveofourown.org→agento3.miscs.dev - CORS 全开
- 端点:
/health/stats/metrics/robots.txt
4. 缓存 (cache.py)
- LRU 实现,容量 5000
- TTL 按路径差异化(首页30s, 章节120s, 图片600s)
- 每个 worker 独立
5. 统计系统 (stats.py) v2
- 内存热路径:计时器 + 桶式计数 (1s 粒度)
- SQLite 批量写入:每 60s flush
- 统计面板新增:Cookie 代理数
6. URL 重写器 (url_rewriter.py)
- HTML/CSS/JS 中
archiveofourown.org→agento3.miscs.dev - 响应头 Location/Set-Cookie 重写
- CF 挑战页面特殊 URL 处理
代理策略
TLS 指纹(按优先级)
safari15_5— 最高 CF 绕过率safari17_0— 次优chrome123— 稳定chrome124— 稳定
已弃用:chrome110, chrome116 (100% 被CF拦截), chrome120, edge120 (低成功率)
代理分级
- Fast Pool (50): 最快代理,用于 POST/登录/注册 (8s timeout, 1 retry)
- Main Pool (676): 全部可用代理,用于 GET/浏览 (15s timeout, 2 retries)
- Cookie 池: 持有 cf_clearance 的代理,优先使用
代理扫描器 (scripts/scan_proxies_cffi.py)
- curl_cffi 2 种浏览器指纹 (chrome123/124)
- 50 并发线程
- 每 30 分钟 cron 自动扫描
- 仅扫描最佳端口范围 13500-14499 (72.6% 成功率)
- 结果排序(快→慢)
负载均衡
Caddy (agento3.miscs.dev:443)
├── Round Robin → 127.0.0.1:8081
└── Round Robin → 127.0.0.1:8082
自维护框架(被动模式)
| 层级 | 类型 | 频率 | 作用 |
|---|---|---|---|
| daemon.py | systemd service | 30s 轮询 | Worker 被动监督,3 次挂才重启 |
| 代理刷新 | Hermes cron | 30min | 重新扫描代理池 |
| 统计报告 | Hermes cron | 1h | 聚合指标 + 异常告警 |
不运行:e2e 测试、Caddy 检查、文件日志、主动健康检查全部 726 代理
关键路径
/home/ubuntu/
├── proxy.txt # 5000 原始代理
├── ao3-mirror/
│ ├── app.py # FastAPI v4 (cookie-aware + CF solver)
│ ├── proxy_pool.py # 异步代理池 v4 (cookie jar)
│ ├── ao3_fetcher.py # 异步抓取器 v4 (CF detection)
│ ├── cache.py # LRU 缓存 (5000)
│ ├── stats.py # 内存热路径统计 v2
│ ├── url_rewriter.py # URL 重写
│ ├── Caddyfile # Caddy 配置
│ ├── start.sh / stop.sh
│ ├── ao3-mirror.service / ao3-daemon.service
│ ├── SOUL.md ← 本文档
│ ├── scripts/
│ │ ├── daemon.py # 自维护守护进程
│ │ ├── scan_proxies_cffi.py # TLS 指纹代理扫描
│ │ └── restart_workers.py # Worker 重启工具
│ └── worker-*.log # Per-worker stdout logs
/dev/shm/
├── working_proxies.txt # 有效代理
├── ao3_stats.db # 统计数据(冷存储)
└── ao3/
└── status.json # 守护进程状态
已知限制
- 每个 worker 独立缓存,无共享层
- Cookie jar 是内存存储,worker 重启后丢失
- 用户浏览器求解 CF 挑战需要 120s 内完成(token TTL)
- 挑战页面 JS 执行依赖用户浏览器环境
下一步
- Cookie 感知代理层 (v4)
- CF 挑战检测与分离 (v4)
- 用户浏览器 CF 挑战求解 (v4)
- 共享缓存层 (Redis/memcached) 跨 worker
- Cookie jar 持久化 (重启后恢复)
- 多 proxy provider 源
- 预加载热门页面到缓存
- Worker 间 cookie jar 同步