Codex API网关迁移与流量优化实战指南
背景
最近我将一个自建的 AI API 网关从旧服务器迁移到新服务器,期间遇到了一些典型的运维问题,包括:

- 数据库从 3.3GB 压缩到 30MB
- 日均流量从 20GB 降到 10GB 以下
- 反向代&理从 Nginx 切换到 Caddy
- 数据库异地自动备份
这篇文章记录了我遇到的问题和解决方案,供有类似需求的朋友参考。
一、数据库迁移:只带业务数据,不带日志
初始数据库有 3.3GB,但其中 90% 以上是无价值的日志数据。通过 pg_dump 排除日志表,核心业务数据只有 几十 MB。
排除大日志表
-- 找出哪些表占空间最大
SELECT table_name,
pg_size_pretty(pg_total_relation_size(table_name::text)) as total_size
FROM (SELECT tablename FROM pg_tables WHERE schemaname = 'public') t
ORDER BY pg_total_relation_size(table_name::text) DESC
LIMIT 10;
结果发现前 5 张日志表占了绝大部分空间:
| 表 | 大小 | 说明 |
|---|---|---|
| ops_system_logs | 2.2 GB | 系统操作日志 |
| usage_logs | 425 MB | API 调用记录 |
| usage_billing_dedup | 273 MB | 计费去重 |
| ops_error_logs | 260 MB | 错误日志 |
| scheduler_outbox | 63 MB | 调度队列 |
pg_dump 排除特定表
pg_dump -U user -d database -T ops_system_logs -T ops_error_logs -T usage_logs -T scheduler_outbox --no-owner --no-acl -Fc -f backup.dump
注意:如果应用代码里引用了这些表的索引或外键(比如 billing_usage_entries 引用了 usage_logs.id),需要在目标库手动补上空表结构,只建表不插数据:
# 从源库导出这些表的 schema(不含数据) pg_dump -U user -d database -t ops_system_logs --schema-only > missing_tables.sql # 在目标库执行 psql -U user -d database < missing_tables.sql
效果
| 指标 | 迁移前 | 迁移后 |
|---|---|---|
| 数据库大小 | 3.3 GB | 31 MB |
| 用户数 | 3,234 | 3,234(一致 ✅) |
| 迁移耗时 | — | 15 秒 |
二、Nginx → Caddy 切换
原服务器使用 Nginx 做反向代&理,切换到 Caddy 后获得了几个好处:
为什么换 Caddy
- 自动 HTTPS — 无需手动申请和续期 Let’s Encrypt
- 内置 zstd 压缩 — 比 gzip 压缩率更高
- 更简洁的配置 — 优雅的 Caddyfile 语法
- 原生 HTTP/2 和 HTTP/3 支持
Caddyfile 配置
api.example.com {
# 反向代&理到后端
reverse_proxy localhost:8080 {
health_uri /health
health_interval 30s
health_timeout 10s
health_status 200
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
transport http {
keepalive 120s
keepalive_idle_conns 256
read_buffer 16KB
write_buffer 16KB
}
fail_duration 30s
max_fails 3
unhealthy_status 500 502 503 504
}
# 压缩配置(zstd + gzip 双支持)
encode {
zstd
gzip 6
minimum_length 256
match {
header Content-Type application/json*
header Content-Type application/javascript*
header Content-Type text/*
}
}
# 请求体限制
request_body {
max_size 50MB
}
}
压缩效果对比
以 JSON API 响应(35KB)为例:
| 压缩方式 | 大小 | 压缩比 |
|---|---|---|
| 无压缩 | 35.7 KB | — |
| gzip | 15.7 KB | 56% |
| zstd | 15.9 KB | 55% |
实际测试对文本类 API 响应,压缩率在 50-70%。
三、流量分析:找出真正的消耗来源
问题场景
服务器流量消耗异常快,怀疑是被攻击或有异常请求。
排查步骤
1. 检查 nginx/Caddy 日志中的实际流量
# 统计每日流量
sudo awk '{date=substr($4,2,11); bytes[date]+=$10}
END{for(d in bytes) printf "%s %.2f GBn", d, bytes[d]/1024/1024/1024}'
/var/log/caddy/api.log | sort
2. 按 URL 路径分析流量分布
sudo awk '{urls[$7]+=$10; count[$7]++}
END{for(u in urls) printf "%.2f GB (%d reqs) %sn", urls[u]/1024/1024/1024, count[u], u}'
/var/log/caddy/api.log | sort -rn | head -10
3. 找出单次响应异常的请求
sudo awk '$7 == "/responses" {if($10>max){max=$10; line=$0}}
END{printf "最大响应: %.1f MBn%sn", max/1024/1024, line}' access.log
发现
/responses(AI 聊天 API)占流量的 80% 以上- 有人单次请求生成了 289.7 MB 的响应(疑似图片生成)
- 大部分用户平均响应只有 220KB
- 请求体限制之前配置为 256MB,过于宽松
优化措施
| 措施 | 效果 |
|---|---|
| 开启 zstd/gzip 压缩 | API 响应缩小 50-60% |
| 请求体限制 256MB → 50MB | 防止单次异常消耗 |
| 给高消耗用户加 RPM 限速 | 控制总请求量 |
四、自动备份脚本
每天凌晨自动备份数据库,排除日志表,保留 7 天。备份同时传输到远程服务器做异地容灾。
#!/bin/bash set -euo pipefail BACKUP_DIR="/data/backups" REMOTE_SERVER="[email protected]" RETENTION_DAYS=7 # 排除的日志表 EXCLUDE_TABLES=( ops_system_logs ops_error_logs usage_logs scheduler_outbox usage_billing_dedup ) mkdir -p "$BACKUP_DIR" BACKUP_FILE="db_backup_$(date +%Y%m%d_%H%M%S).dump" # 构建排除参数 EXCLUDE_ARGS="" for table in "${EXCLUDE_TABLES[@]}"; do EXCLUDE_ARGS="$EXCLUDE_ARGS -T $table" done # 备份 docker exec postgres pg_dump -U user -d database $EXCLUDE_ARGS --no-owner --no-acl -Fc > "$BACKUP_PATH" # 验证备份完整性 docker run --rm postgres:18-alpine pg_restore -l "$BACKUP_FILE" || exit 1 # 传输到远程 scp "$BACKUP_PATH" "${REMOTE_SERVER}:${BACKUP_DIR}/" # 清理过期备份 find "$BACKUP_DIR" -name "*.dump" -type f -mtime +$RETENTION_DAYS -delete
设置定时任务:
echo "0 5 * * * root /opt/scripts/daily_backup.sh" > /etc/cron.d/db-backup
五、常见问题排查
503 Service Unavailable
切换到 Caddy 后可能遇到 503:
原因:Caddy 的健康检查发现后端不可用后,会将后端标记为不可用一段时间(fail_duration 30s)。
解决:
# 重启后端后需要重载 Caddy caddy reload --config /etc/caddy/Caddyfile
或者修改健康检查配置,降低判定阈值:
reverse_proxy localhost:8080 {
health_uri /health
health_interval 10s
health_timeout 5s
fail_duration 10s
max_fails 1
}
413 Payload Too Large
请求体超出限制时返回 413。
排查:
# 查看 nginx/Caddy 错误日志 grep "413" /var/log/caddy/*.log # 确认当前限制值 grep max_size /etc/caddy/Caddyfile
如果通过 Cloudflare,还需要注意 Cloudflare 免费版限制了 100MB 的最大请求体,超过会被 Cloudflare 直接拦截。
总结
这次迁移总结了几点经验:
- 数据库日志要定期清理 — 设计好保留策略,不然日志会占满磁盘
- 反向代&理优先选 Caddy — 配置简单,自动 HTTPS,自带 zstd 压缩
- 流量分析要找源头 — 不要盲目扩带宽,先看流量花在哪
- 备份要验证 — 用
pg_restore -l检查备份完整性,否则等于没备份 - 异地备份 — 主备两台服务器互相备份,防止单点故障
代码和配置示例仅供参考,实际部署需要根据具体环境调整。
-
07.04
苏丹的游戏手机版奈布哈尼收服攻略 苏丹的游戏手机版奈布哈尼角色获取与培养方法
-
07.04
苏丹的游戏手机版:穷人到底需要什么通关攻略与核心需求解析
-
07.04
梦之形手机版清醒梦解锁方法:梦之形手机版如何触发并进入清醒梦
-
07.04
苏丹的游戏手机版芮尔剧情攻略:苏丹的游戏手机版芮尔完整剧情流程与关键选择解析
-
07.04
毁灭之井梦之形手机版毁灭之井梦之形手机版玩法机制与通关技巧详解
-
07.04
梦之形手机版獠牙 梦之形手机版獠牙 角色设定与技能解析
-
-
- 儿童手表变天:AI要革小天才的命?
- 07.04
-
- 冲刺Token工厂第一股:硅基流动越亏越值钱?
- 07.04
-
- 真正有价值的AI Agent:必须长在业务流程里
- 07.04
-
- 题意形式化
- 07.04
-
-
-
下载
- 《神剑伏魔录》(神剑风云)游戏音乐合集
- 其他游戏|7.73 MB
- 一款非常好玩的武侠闯关游戏
-
-
下载
- 《行尸走肉第一章》免安装中文汉化硬盘版下载
- 单机|436 MB
- 一款以动作冒险为主题的游戏
-
-
下载
- 《街头霸王X铁拳》免安装中文汉化硬盘版下载
- 单机|111MB
- 一款非常好玩的格斗游戏
-
-
下载
- 《生化危机:浣熊市行动》免安装中文硬盘版下载
- 单机|6310 MB
- 一款以动作射击为主题的游戏
-
-
下载
- 《暗黑破坏神3》免安装繁体中文正式版下载
- 单机|7630 MB
- 一款以角色扮演为主题的游戏
-
-
下载
- 《马克思佩恩3》免安装硬盘版下载
- 单机|27033 MB
- 一款以第三人称射击为主题的游戏