Files
agento3/SOUL.md

186 lines
8.1 KiB
Markdown
Raw Permalink Normal View History

# 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
- 智能重试策略:
1. 首次尝试 → 优先用带 cookie 的代理
2. 遇到 CF 挑战 → 换代理(不退避)
3. 遇到真失败 → 退避 + 换代理
- 重试 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_token` cookie 和 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 指纹(按优先级)
1. `safari15_5` — 最高 CF 绕过率
2. `safari17_0` — 次优
3. `chrome123` — 稳定
4. `chrome124` — 稳定
**已弃用**chrome110, chrome116 (100% 被CF拦截), chrome120, edge120 (低成功率)
### 代理分级
1. **Fast Pool** (50): 最快代理,用于 POST/登录/注册 (8s timeout, 1 retry)
2. **Main Pool** (676): 全部可用代理,用于 GET/浏览 (15s timeout, 2 retries)
3. **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 执行依赖用户浏览器环境
## 下一步
- [x] Cookie 感知代理层 (v4)
- [x] CF 挑战检测与分离 (v4)
- [x] 用户浏览器 CF 挑战求解 (v4)
- [ ] 共享缓存层 (Redis/memcached) 跨 worker
- [ ] Cookie jar 持久化 (重启后恢复)
- [ ] 多 proxy provider 源
- [ ] 预加载热门页面到缓存
- [ ] Worker 间 cookie jar 同步