
本文解决一个具体问题:如何让品牌AI可见度监测系统中的定时采集任务稳定执行,避免任务堆积、重复执行和失败无补偿。使用Celery+Redis作为任务调度框架,结合关系型数据库实现幂等控制,并设计基于错误类型的重试策略。适合需要构建定时采集系统的后端开发者阅读。前提是需要了解Python、Celery基本概念和Redis基础操作。
只调用一次模型接口并不复杂,但真正进入品牌监测系统后,还要处理任务调度、重试、幂等和监控。初期我们采用简单Cron Job直接调用模型接口,很快遇到问题:
根本原因在于:
对比了几个方案:
方案 | 优点 | 缺点 |
|---|---|---|
Celery + Redis | 成熟、社区活跃、支持任务优先级和定时 | 需要维护Redis和Worker集群 |
云函数 + 云数据库 | Serverless、免运维、自动扩缩 | 执行时长有限制,不适合长任务 |
自建任务队列(Redis List + Worker) | 灵活、可控、无外部依赖 | 需要自行实现调度和重试逻辑 |
综合考虑团队技术栈和运维能力,选择Celery + Redis作为任务调度框架。数据库使用MySQL存储任务状态和结果,具体云服务可替换为项目实际使用的对应服务,控制台路径和参数请以官方文档为准。
定义任务模型,使用Celery的bind=True使任务实例可访问自身状态:
# tasks.py
from celery import Celery
app = Celery('brand_monitor', broker='redis://localhost:6379/0')
@app.task(bind=True, max_retries=3, default_retry_delay=60)
def query_brand_visibility(self, brand_id, question_id):
"""
查询品牌在指定问题下的AI回答可见度。
参数:
brand_id: 品牌ID
question_id: 问题ID
"""
try:
result = call_ai_model(brand_id, question_id)
save_result(brand_id, question_id, result)
return {'status': 'success', 'brand_id': brand_id, 'question_id': question_id}
except Exception as exc:
# 指数退避重试
raise self.retry(exc=exc, countdown=2 ** self.request.retries * 60)代码说明:
max_retries=3限制最大重试次数。default_retry_delay=60初始重试延迟60秒,后续通过countdown实现指数退避(2^retries * 60秒)。self.retry()触发重试,Celery自动管理重试计数和延迟。使用Celery Beat实现定时调度,调度配置存储在数据库中,支持动态调整:
# celerybeat_schedule.py
from celery.schedules import crontab
app.conf.beat_schedule = {
'query-every-hour': {
'task': 'tasks.query_brand_visibility',
'schedule': crontab(minute=0, hour='*/1'),
'args': (brand_id, question_id)
},
}实际生产中,调度配置存储在数据库中,支持动态调整执行频率和启用/禁用任务。
在数据库中为每次查询结果设置唯一约束,避免重复写入:
CREATE TABLE brand_visibility (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
brand_id INT NOT NULL,
question_id INT NOT NULL,
query_time DATETIME NOT NULL,
result JSON,
UNIQUE KEY uk_brand_question_time (brand_id, question_id, query_time)
);任务执行时,先检查是否已存在相同brand_id + question_id + query_time的记录,若存在则跳过。
根据错误类型采用不同重试策略:
Retry-After头指定的时间后重试。from requests.exceptions import Timeout, HTTPError
@app.task(bind=True, max_retries=3)
def query_brand_visibility(self, brand_id, question_id):
try:
response = call_ai_model(brand_id, question_id, timeout=30)
response.raise_for_status()
result = response.json()
save_result(brand_id, question_id, result)
except HTTPError as exc:
if exc.response.status_code == 429:
retry_after = int(exc.response.headers.get('Retry-After', 60))
raise self.retry(exc=exc, countdown=retry_after)
elif exc.response.status_code >= 500:
raise self.retry(exc=exc, countdown=2 ** self.request.retries * 60)
else:
log_error(brand_id, question_id, exc)
except Timeout:
if self.request.retries < 2:
raise self.retry(exc=exc, countdown=10, kwargs={'timeout': 60})
else:
log_error(brand_id, question_id, exc)
except Exception as exc:
log_error(brand_id, question_id, exc)正常情况下应当看到以下现象:
本文解决了品牌AI可见度监测系统中任务调度与重试的工程问题。关键实现包括:使用Celery+Redis构建任务队列,通过唯一约束实现幂等控制,按错误类型设计差异化重试策略,并建立监控告警体系。本方案适用于需要定时采集外部API数据的场景,但需注意,在任务量超过万级时可能需要引入分区策略。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。