业务任务与云上约束
当AI回答采集系统从本地原型走向生产环境,你很快会发现:本地跑通的脚本,到了云上可能因为网络延迟、资源争抢、限流或数据一致性而崩溃。本文记录了一次完整的迁移过程——将一套基于Python的AI回答采集系统从单机原型迁移到阿里云ECS + Redis + RDS的生产架构,重点解决并发控制、数据持久化和成本平衡问题。
环境和资源准备
云资源:阿里云ECS(2核4G,CentOS 7.9)、Redis标准版(2GB)、RDS MySQL(2核4G,20GB SSD)
本地原型:Python 3.9 + requests + sqlite3,单线程串行采集
目标架构:多线程并发采集 + Redis任务队列 + MySQL持久化 + 日志监控
约束条件:单次采集任务不超过1000个目标URL,单日总请求量不超过10万次,预算月均200元以内
问题现象或目标
本地原型在采集100个URL时耗时约15分钟,且sqlite3在并发写入时频繁报错“database is locked”。迁移到云上后,需要支持至少10个并发线程,同时保证数据不丢失、请求不超时、成本可控。
方案对比与选择
方案 优点 缺点
直接多线程 + MySQL 简单 连接池管理复杂,高并发下锁竞争严重
Redis队列 + 多Worker 解耦,可水平扩展 需要额外维护Redis
消息队列(RocketMQ) 功能强大 成本高,运维复杂
最终选择Redis队列 + 多Worker方案:Redis作为任务缓冲和去重,多个Worker进程从队列拉取任务并写入MySQL。
核心实现
- 任务队列设计
使用Redis的List结构作为任务队列,Sadd用于URL去重。
import redis
r = redis.Redis(host='your-redis-host', port=6379, decode_responses=True)
生产者:将URL推入队列
def push_tasks(urls):
for url in urls:
if r.sadd('url_set', url): # 去重
r.lpush('task_queue', url)
- Worker实现
Worker从队列右侧阻塞弹出任务,采集后写入MySQL。
import pymysql
from bs4 import BeautifulSoup
import requests
def worker():
conn = pymysql.connect(host='your-rds-host', user='user', password='pass', db='ai_collect')
cursor = conn.cursor()
while True:
url = r.brpop('task_queue', timeout=5)
if not url:
break
url = url[1]
try:
resp = requests.get(url, timeout=10)
soup = BeautifulSoup(resp.text, 'html.parser')
# 解析逻辑...
cursor.execute("INSERT INTO results (url, content) VALUES (%s, %s)", (url, content))
conn.commit()
except Exception as e:
# 失败重入队列(最多重试3次)
retry_key = f'retry:{url}'
retry_count = r.get(retry_key)
if retry_count and int(retry_count) >= 3:
r.lpush('dead_letter', url)
else:
r.incr(retry_key)
r.lpush('task_queue', url)
cursor.close()
conn.close()
- 并发控制
使用Python的multiprocessing启动多个Worker进程,数量根据ECS核数设定为4。
from multiprocessing import Process
processes = []
for _ in range(4):
p = Process(target=worker)
p.start()
processes.append(p)
for p in processes:
p.join()
测试与监控结果
性能:1000个URL采集耗时从单线程的150分钟降至约20分钟(4 Worker)。
稳定性:连续运行24小时,无数据丢失,死信队列中仅有3个URL(因目标网站屏蔽)。
监控:使用阿里云CloudMonitor监控ECS CPU(平均40%)、Redis内存(1.2GB)、RDS连接数(峰值15)。
成本、稳定性和安全分析
成本:ECS 约80元/月 + Redis 约60元/月 + RDS 约50元/月 = 190元/月,符合预算。
稳定性:Redis队列解耦了生产者和消费者,单个Worker崩溃不影响其他Worker;死信队列防止任务无限重试。
安全:RDS白名单限制只允许ECS访问;Redis设置密码和绑定内网IP。
踩坑与适用边界
踩坑1:Redis的brpop默认阻塞,如果所有Worker都阻塞,任务到来时可能延迟。解决方案:设置较短的timeout(如5秒),并配合定期心跳。
踩坑2:MySQL连接未关闭导致连接泄漏。解决方案:使用连接池(如pymysql.pooling)。
适用边界:本方案适用于中小规模采集(日请求量<10万),如果超过百万级,建议改用消息队列(如RocketMQ)和分布式Worker。
可复用清单
使用Redis List + Set实现任务队列和去重。
多进程Worker模型,进程数不超过CPU核数。
失败重试机制,最多重试3次后进入死信队列。
监控ECS、Redis、RDS的关键指标。
设置合理的超时和重试策略,避免雪崩。
通过以上步骤,你可以将AI回答采集系统从本地原型平滑迁移到云上,获得更高的并发能力和稳定性,同时控制成本。