目录
- [使用Celery处理Python Web应用中的异步任务](#使用Celery处理Python Web应用中的异步任务)
-
- [1. 引言](#1. 引言)
-
- [1.1 异步任务的重要性](#1.1 异步任务的重要性)
- [1.2 Celery简介](#1.2 Celery简介)
- [2. Celery基础概念](#2. Celery基础概念)
-
- [2.1 核心组件架构](#2.1 核心组件架构)
- [2.2 任务执行流程](#2.2 任务执行流程)
- [3. 环境配置与安装](#3. 环境配置与安装)
-
- [3.1 安装依赖](#3.1 安装依赖)
- [3.2 消息代理选择与配置](#3.2 消息代理选择与配置)
- [4. 基础用法与实践](#4. 基础用法与实践)
-
- [4.1 最小化Celery应用](#4.1 最小化Celery应用)
- [4.2 基础任务示例](#4.2 基础任务示例)
- [5. 高级特性与最佳实践](#5. 高级特性与最佳实践)
-
- [5.1 任务重试与错误处理](#5.1 任务重试与错误处理)
- [5.2 任务工作流与链式操作](#5.2 任务工作流与链式操作)
- [6. Web框架集成](#6. Web框架集成)
-
- [6.1 Flask集成示例](#6.1 Flask集成示例)
- [6.2 Django集成示例](#6.2 Django集成示例)
- [7. 监控与管理](#7. 监控与管理)
-
- [7.1 使用Flower监控Celery](#7.1 使用Flower监控Celery)
- [7.2 自定义监控指标](#7.2 自定义监控指标)
- [8. 性能优化与最佳实践](#8. 性能优化与最佳实践)
-
- [8.1 配置优化](#8.1 配置优化)
- [8.2 部署配置](#8.2 部署配置)
- [9. 完整代码示例](#9. 完整代码示例)
-
- [9.1 项目结构](#9.1 项目结构)
- [9.2 完整的Celery应用配置](#9.2 完整的Celery应用配置)
- [9.3 依赖文件](#9.3 依赖文件)
- [10. 总结](#10. 总结)
-
- [10.1 核心优势](#10.1 核心优势)
- [10.2 性能指标对比](#10.2 性能指标对比)
- [10.3 最佳实践总结](#10.3 最佳实践总结)
『宝藏代码胶囊开张啦!』------ 我的 CodeCapsule 来咯!✨
写代码不再头疼!我的新站点 CodeCapsule 主打一个 "白菜价"+"量身定制 "!无论是卡脖子的毕设/课设/文献复现 ,需要灵光一现的算法改进 ,还是想给项目加个"外挂",这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网
使用Celery处理Python Web应用中的异步任务
1. 引言
1.1 异步任务的重要性
在现代Web应用开发中,用户体验和系统性能是至关重要的考量因素。传统的同步请求-响应模式在处理耗时操作时会显著降低用户体验,比如:
- 用户注册时发送验证邮件
- 处理大型文件上传和解析
- 生成复杂的报表和数据导出
- 调用第三方API服务
- 执行数据分析和机器学习推理
这些操作如果采用同步方式处理,会导致用户长时间等待,甚至出现请求超时。异步任务处理通过将耗时操作转移到后台执行,让Web应用能够立即响应用户请求,大大提升了用户体验和系统吞吐量。
1.2 Celery简介
Celery是一个基于分布式消息传递的异步任务队列/作业队列,专注于实时处理,同时支持任务调度。它的核心特性包括:
- 分布式架构:支持多工作进程和服务器
- 灵活的消息传输:支持Redis、RabbitMQ、Amazon SQS等
- 任务调度:支持定时任务和周期性任务
- 结果存储:支持多种后端存储任务结果
- 监控和管理:提供丰富的监控工具
Celery的典型应用场景包括:
- 后台任务处理
- 定时任务调度
- 分布式计算
- 工作流管理
2. Celery基础概念
2.1 核心组件架构
Celery架构包含以下几个核心组件:
Celery生态系统 发送任务 任务队列 执行任务 存储结果 查询结果 监控工具 Flower 定时任务 Beat Client/Web应用 消息代理 Broker Celery Worker 任务结果后端
各个组件的功能说明:
- Client:应用程序代码,负责发送任务
- Broker:消息中间件,负责接收和分发任务
- Worker:工作进程,负责执行任务
- Backend:存储任务状态和结果
- Beat:定时任务调度器
- Flower:实时监控工具
2.2 任务执行流程
任务在Celery中的完整执行流程可以用以下公式表示:
Task Lifecycle = Submit → Queue → Execute → Result \text{Task Lifecycle} = \text{Submit} \rightarrow \text{Queue} \rightarrow \text{Execute} \rightarrow \text{Result} Task Lifecycle=Submit→Queue→Execute→Result
具体步骤:
- 任务提交 :Client调用
task.delay()
或task.apply_async()
- 消息序列化:任务参数被序列化并发送到Broker
- 任务排队:Broker将任务放入相应的队列
- 任务获取:Worker从Broker获取任务
- 任务执行:Worker执行任务函数
- 结果存储:执行结果存储到Backend
- 结果查询 :Client可以通过
AsyncResult
查询任务状态和结果
3. 环境配置与安装
3.1 安装依赖
首先安装必要的依赖包:
bash
pip install celery redis flower
对于生产环境,建议使用固定版本:
bash
pip install celery==5.3.4 redis==4.6.0 flower==1.2.0
3.2 消息代理选择与配置
Celery支持多种消息代理,以下是主要选项的对比:
代理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Redis | 安装简单、性能高 | 持久化需要配置 | 开发、测试、中小项目 |
RabbitMQ | 功能丰富、稳定可靠 | 配置复杂、资源消耗大 | 生产环境、企业级应用 |
Amazon SQS | 完全托管、可扩展 | 成本较高、延迟较大 | AWS环境、云原生应用 |
这里我们选择Redis作为消息代理,安装和配置:
bash
# 安装Redis服务器
sudo apt-get install redis-server
# 启动Redis
redis-server
# 或者使用Docker
docker run -d -p 6379:6379 redis:alpine
4. 基础用法与实践
4.1 最小化Celery应用
创建一个基础的Celery应用:
python
# celery_app.py
import os
from celery import Celery
from datetime import timedelta
# 设置环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# 创建Celery应用实例
app = Celery(
'myproject',
broker='redis://localhost:6379/0', # Redis作为消息代理
backend='redis://localhost:6379/1', # Redis作为结果后端
include=[ # 包含任务模块
'tasks.email_tasks',
'tasks.file_tasks',
'tasks.analytics_tasks'
]
)
# 配置Celery
app.conf.update(
# 基础配置
task_serializer='json',
accept_content=['json'],
result_serializer='json',
timezone='Asia/Shanghai',
enable_utc=True,
# 任务路由配置
task_routes={
'tasks.email_tasks.*': {'queue': 'email'},
'tasks.file_tasks.*': {'queue': 'files'},
'tasks.analytics_tasks.*': {'queue': 'analytics'},
},
# 任务结果配置
result_expires=3600, # 结果过期时间1小时
# Worker配置
worker_prefetch_multiplier=4, # 预取任务数量
worker_max_tasks_per_child=1000, # 每个子进程最大任务数
)
if __name__ == '__main__':
app.start()
4.2 基础任务示例
创建各种类型的任务:
python
# tasks/email_tasks.py
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from celery import Celery
from celery_app import app
import time
import logging
logger = logging.getLogger(__name__)
@app.task(bind=True, max_retries=3, default_retry_delay=60)
def send_email(self, to_email, subject, body, html_body=None):
"""
发送邮件任务
Args:
to_email: 收件人邮箱
subject: 邮件主题
body: 邮件正文(纯文本)
html_body: 邮件正文(HTML格式)
"""
try:
# 模拟邮件配置(实际使用时替换为真实配置)
smtp_server = "smtp.example.com"
port = 587
sender_email = "noreply@example.com"
password = "your_password"
# 创建邮件
message = MIMEMultipart("alternative")
message["Subject"] = subject
message["From"] = sender_email
message["To"] = to_email
# 添加纯文本版本
part1 = MIMEText(body, "plain")
message.attach(part1)
# 添加HTML版本(如果提供)
if html_body:
part2 = MIMEText(html_body, "html")
message.attach(part2)
# 发送邮件
with smtplib.SMTP(smtp_server, port) as server:
server.starttls()
server.login(sender_email, password)
server.sendmail(sender_email, to_email, message.as_string())
logger.info(f"邮件发送成功: {to_email}")
return {"status": "success", "message": f"邮件已发送至 {to_email}"}
except smtplib.SMTPException as e:
logger.error(f"邮件发送失败: {str(e)}")
# 重试逻辑
raise self.retry(exc=e, countdown=60)
@app.task
def send_bulk_emails(emails_data):
"""
批量发送邮件任务
Args:
emails_data: 邮件数据列表,每个元素包含to_email, subject, body
"""
results = []
for email_data in emails_data:
try:
# 为每个邮件创建单独的任务
result = send_email.delay(
email_data['to_email'],
email_data['subject'],
email_data['body']
)
results.append(result.id)
except Exception as e:
logger.error(f"创建邮件任务失败: {str(e)}")
results.append(None)
return {
"total_emails": len(emails_data),
"task_ids": results,
"status": "processing"
}
文件处理任务:
python
# tasks/file_tasks.py
import os
import pandas as pd
from celery import Celery
from celery_app import app
import time
import logging
from pathlib import Path
logger = logging.getLogger(__name__)
@app.task(bind=True)
def process_csv_file(self, file_path, output_path=None):
"""
处理CSV文件任务
Args:
file_path: 输入文件路径
output_path: 输出文件路径
"""
try:
# 更新任务状态
self.update_state(
state='PROGRESS',
meta={'current': 0, 'total': 100, 'status': '开始读取文件'}
)
# 读取CSV文件
df = pd.read_csv(file_path)
self.update_state(
state='PROGRESS',
meta={'current': 30, 'total': 100, 'status': '文件读取完成,开始处理数据'}
)
# 模拟数据处理
time.sleep(2)
# 数据清洗和处理
# 1. 处理缺失值
df.fillna(method='ffill', inplace=True)
# 2. 数据类型转换
numeric_columns = df.select_dtypes(include=['object']).columns
for col in numeric_columns:
try:
df[col] = pd.to_numeric(df[col], errors='ignore')
except:
pass
self.update_state(
state='PROGRESS',
meta={'current': 70, 'total': 100, 'status': '数据清洗完成,开始生成报告'}
)
# 生成统计报告
report = {
'total_rows': len(df),
'total_columns': len(df.columns),
'column_names': list(df.columns),
'data_types': dict(df.dtypes),
'missing_values': dict(df.isnull().sum()),
'basic_stats': df.describe().to_dict() if len(df.select_dtypes(include=['number']).columns) > 0 else {}
}
# 保存处理后的文件
if output_path:
df.to_csv(output_path, index=False)
report['output_file'] = output_path
self.update_state(
state='PROGRESS',
meta={'current': 100, 'total': 100, 'status': '任务完成'}
)
return report
except Exception as e:
logger.error(f"CSV文件处理失败: {str(e)}")
return {'status': 'error', 'message': str(e)}
@app.task
def generate_excel_report(data, output_path):
"""
生成Excel报表任务
Args:
data: 报表数据
output_path: 输出文件路径
"""
try:
# 确保输出目录存在
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# 创建Excel writer
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
# 写入多个sheet
if isinstance(data, dict):
for sheet_name, sheet_data in data.items():
if isinstance(sheet_data, list):
df = pd.DataFrame(sheet_data)
df.to_excel(writer, sheet_name=sheet_name, index=False)
elif isinstance(sheet_data, pd.DataFrame):
sheet_data.to_excel(writer, sheet_name=sheet_name, index=False)
return {
'status': 'success',
'output_path': output_path,
'file_size': os.path.getsize(output_path)
}
except Exception as e:
logger.error(f"生成Excel报表失败: {str(e)}")
return {'status': 'error', 'message': str(e)}
5. 高级特性与最佳实践
5.1 任务重试与错误处理
实现健壮的任务重试机制:
python
# tasks/advanced_tasks.py
import requests
from celery import Celery
from celery_app import app
import logging
from time import sleep
from requests.exceptions import RequestException
logger = logging.getLogger(__name__)
class CircuitBreaker:
"""简单的熔断器实现"""
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.failure_count = 0
self.last_failure_time = None
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
def can_execute(self):
if self.state == "OPEN":
if self.last_failure_time and \
(time.time() - self.last_failure_time) > self.recovery_timeout:
self.state = "HALF_OPEN"
return True
return False
return True
def record_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
def record_success(self):
self.failure_count = 0
self.state = "CLOSED"
# 创建熔断器实例
api_circuit_breaker = CircuitBreaker()
@app.task(bind=True, max_retries=5, default_retry_delay=30)
def call_external_api(self, url, method='GET', data=None, headers=None):
"""
调用外部API任务,包含重试和熔断机制
"""
# 检查熔断器状态
if not api_circuit_breaker.can_execute():
logger.warning(f"熔断器开启,跳过API调用: {url}")
raise self.retry(countdown=60)
try:
# 设置请求参数
request_kwargs = {
'timeout': 30,
'headers': headers or {}
}
if method.upper() == 'GET':
response = requests.get(url, **request_kwargs)
elif method.upper() == 'POST':
request_kwargs['json'] = data
response = requests.post(url, **request_kwargs)
else:
raise ValueError(f"不支持的HTTP方法: {method}")
# 检查响应状态
response.raise_for_status()
# 记录成功
api_circuit_breaker.record_success()
return {
'status': 'success',
'status_code': response.status_code,
'data': response.json() if response.content else None
}
except RequestException as e:
# 记录失败
api_circuit_breaker.record_failure()
logger.error(f"API调用失败: {str(e)}")
# 根据错误类型决定重试策略
if isinstance(e, requests.exceptions.Timeout):
retry_delay = 10 # 超时快速重试
elif isinstance(e, requests.exceptions.ConnectionError):
retry_delay = 30 # 连接错误中等重试
else:
retry_delay = 60 # 其他错误慢速重试
# 计算重试次数和延迟
retry_count = self.request.retries
exponential_backoff = retry_delay * (2 ** retry_count)
raise self.retry(exc=e, countdown=min(exponential_backoff, 300))
@app.task(bind=True)
def task_with_fallback(self, main_task_args, fallback_task_args):
"""
带有降级策略的任务
"""
try:
# 尝试执行主要任务
result = call_external_api.delay(**main_task_args)
# 设置超时
try:
return result.get(timeout=30)
except TimeoutError:
logger.warning("主任务超时,执行降级任务")
# 主任务超时,执行降级任务
return execute_fallback_task.delay(**fallback_task_args).get()
except Exception as e:
logger.error(f"任务执行失败: {str(e)}")
# 执行降级任务
return execute_fallback_task.delay(**fallback_task_args).get()
@app.task
def execute_fallback_task(**kwargs):
"""降级任务"""
logger.info("执行降级任务")
return {'status': 'fallback', 'message': '使用降级方案处理'}
5.2 任务工作流与链式操作
创建复杂的任务工作流:
python
# tasks/workflow_tasks.py
from celery import chain, group, chord
from celery_app import app
import logging
logger = logging.getLogger(__name__)
@app.task
def data_processing_workflow(data_source, output_format='excel'):
"""
数据处理的完整工作流
"""
try:
# 创建工作流链
workflow = chain(
validate_data_source.s(data_source),
extract_data.s(),
transform_data.s(),
load_data.s(output_format)
)
# 执行工作流
result = workflow()
return {
'workflow_id': result.id,
'status': 'started',
'message': '数据处理工作流已启动'
}
except Exception as e:
logger.error(f"工作流创建失败: {str(e)}")
return {'status': 'error', 'message': str(e)}
@app.task
def validate_data_source(data_source):
"""验证数据源"""
logger.info(f"验证数据源: {data_source}")
# 模拟验证逻辑
if not data_source or not isinstance(data_source, str):
raise ValueError("无效的数据源")
return {'data_source': data_source, 'valid': True}
@app.task
def extract_data(validation_result):
"""提取数据"""
logger.info("提取数据")
data_source = validation_result['data_source']
# 模拟数据提取
extracted_data = [
{'id': i, 'value': f"data_{i}", 'timestamp': f"2024-01-{i:02d}"}
for i in range(1, 6)
]
return {
'source': data_source,
'data': extracted_data,
'record_count': len(extracted_data)
}
@app.task
def transform_data(extraction_result):
"""转换数据"""
logger.info("转换数据")
data = extraction_result['data']
# 模拟数据转换
transformed_data = []
for record in data:
transformed_record = record.copy()
transformed_record['processed'] = True
transformed_record['transformed_value'] = record['value'].upper()
transformed_data.append(transformed_record)
return {
'original_count': len(data),
'transformed_data': transformed_data,
'transformation_applied': ['uppercase', 'timestamp_processing']
}
@app.task
def load_data(transformation_result, output_format):
"""加载数据"""
logger.info(f"加载数据,输出格式: {output_format}")
data = transformation_result['transformed_data']
# 根据输出格式处理数据
if output_format == 'excel':
result = generate_excel_report.delay(
{'processed_data': data},
f'/tmp/report_{len(data)}_records.xlsx'
)
else:
result = {'status': 'completed', 'format': output_format, 'record_count': len(data)}
return result
@app.task
def parallel_processing_workflow(data_chunks):
"""
并行处理工作流
"""
# 创建并行任务组
parallel_tasks = group(
process_data_chunk.s(chunk)
for chunk in data_chunks
)
# 创建聚合任务(在所有并行任务完成后执行)
workflow = chord(parallel_tasks)(aggregate_results.s())
return {
'workflow_id': workflow.id,
'chunk_count': len(data_chunks),
'status': 'started'
}
@app.task
def process_data_chunk(data_chunk):
"""处理数据块"""
logger.info(f"处理数据块,大小: {len(data_chunk) if data_chunk else 0}")
# 模拟数据处理
processed_chunk = [
{**item, 'processed': True, 'chunk_id': id(item)}
for item in (data_chunk or [])
]
return {
'input_size': len(data_chunk) if data_chunk else 0,
'output_size': len(processed_chunk),
'data': processed_chunk
}
@app.task
def aggregate_results(individual_results):
"""聚合结果"""
logger.info("聚合并行处理结果")
total_records = sum(result['output_size'] for result in individual_results)
successful_chunks = len([r for r in individual_results if r['output_size'] > 0])
return {
'total_processed_records': total_records,
'successful_chunks': successful_chunks,
'total_chunks': len(individual_results),
'success_rate': successful_chunks / len(individual_results) if individual_results else 0,
'aggregation_time': '2024-01-01 12:00:00' # 实际应该使用当前时间
}
6. Web框架集成
6.1 Flask集成示例
在Flask应用中集成Celery:
python
# app.py (Flask应用)
from flask import Flask, request, jsonify, render_template
from celery_app import app as celery_app
from tasks.email_tasks import send_email, send_bulk_emails
from tasks.file_tasks import process_csv_file, generate_excel_report
from tasks.workflow_tasks import data_processing_workflow, parallel_processing_workflow
from celery.result import AsyncResult
import os
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/send-email', methods=['POST'])
def api_send_email():
"""发送邮件API接口"""
try:
data = request.get_json()
# 验证必要参数
required_fields = ['to_email', 'subject', 'body']
for field in required_fields:
if field not in data:
return jsonify({
'status': 'error',
'message': f'缺少必要字段: {field}'
}), 400
# 异步发送邮件
task = send_email.delay(
to_email=data['to_email'],
subject=data['subject'],
body=data['body'],
html_body=data.get('html_body')
)
return jsonify({
'status': 'success',
'message': '邮件发送任务已提交',
'task_id': task.id
}), 202
except Exception as e:
return jsonify({
'status': 'error',
'message': f'请求处理失败: {str(e)}'
}), 500
@app.route('/api/upload-csv', methods=['POST'])
def api_upload_csv():
"""上传并处理CSV文件"""
try:
if 'file' not in request.files:
return jsonify({
'status': 'error',
'message': '没有上传文件'
}), 400
file = request.files['file']
if file.filename == '':
return jsonify({
'status': 'error',
'message': '没有选择文件'
}), 400
if not file.filename.endswith('.csv'):
return jsonify({
'status': 'error',
'message': '只支持CSV文件'
}), 400
# 保存上传的文件
upload_dir = 'uploads'
os.makedirs(upload_dir, exist_ok=True)
file_path = os.path.join(upload_dir, file.filename)
file.save(file_path)
# 异步处理文件
output_path = os.path.join(upload_dir, f'processed_{file.filename}')
task = process_csv_file.delay(file_path, output_path)
return jsonify({
'status': 'success',
'message': '文件处理任务已提交',
'task_id': task.id,
'original_file': file.filename
}), 202
except Exception as e:
return jsonify({
'status': 'error',
'message': f'文件处理失败: {str(e)}'
}), 500
@app.route('/api/task/<task_id>', methods=['GET'])
def get_task_status(task_id):
"""获取任务状态"""
try:
task_result = AsyncResult(task_id, app=celery_app)
response_data = {
'task_id': task_id,
'status': task_result.status,
}
if task_result.status == 'SUCCESS':
response_data['result'] = task_result.result
elif task_result.status == 'FAILURE':
response_data['error'] = str(task_result.result)
elif task_result.status == 'PROGRESS':
response_data['progress'] = task_result.result
return jsonify(response_data)
except Exception as e:
return jsonify({
'status': 'error',
'message': f'获取任务状态失败: {str(e)}'
}), 500
@app.route('/api/batch-process', methods=['POST'])
def api_batch_process():
"""批量数据处理"""
try:
data = request.get_json()
if 'data_chunks' not in data or not isinstance(data['data_chunks'], list):
return jsonify({
'status': 'error',
'message': '缺少data_chunks字段或格式不正确'
}), 400
# 启动并行处理工作流
task = parallel_processing_workflow.delay(data['data_chunks'])
return jsonify({
'status': 'success',
'message': '批量处理工作流已启动',
'task_id': task.id,
'chunk_count': len(data['data_chunks'])
}), 202
except Exception as e:
return jsonify({
'status': 'error',
'message': f'批量处理失败: {str(e)}'
}), 500
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
6.2 Django集成示例
在Django项目中集成Celery:
python
# myproject/celery.py
import os
from celery import Celery
# 设置Django默认设置模块
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
app = Celery('myproject')
# 从Django设置中读取Celery配置
app.config_from_object('django.conf:settings', namespace='CELERY')
# 自动从所有已注册的Django app中发现任务
app.autodiscover_tasks()
@app.task(bind=True, ignore_result=True)
def debug_task(self):
print(f'Request: {self.request!r}')
python
# myproject/__init__.py
from .celery import app as celery_app
__all__ = ('celery_app',)
Django视图示例:
python
# myapp/views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from celery.result import AsyncResult
from .tasks import send_email, process_csv_file, data_processing_workflow
import json
import os
@csrf_exempt
@require_http_methods(["POST"])
def send_email_view(request):
"""发送邮件视图"""
try:
data = json.loads(request.body)
# 验证参数
required_fields = ['to_email', 'subject', 'body']
for field in required_fields:
if field not in data:
return JsonResponse({
'status': 'error',
'message': f'Missing required field: {field}'
}, status=400)
# 异步发送邮件
task = send_email.delay(
to_email=data['to_email'],
subject=data['subject'],
body=data['body'],
html_body=data.get('html_body')
)
return JsonResponse({
'status': 'success',
'message': 'Email task submitted',
'task_id': task.id
}, status=202)
except Exception as e:
return JsonResponse({
'status': 'error',
'message': f'Request processing failed: {str(e)}'
}, status=500)
@csrf_exempt
@require_http_methods(["GET"])
def task_status_view(request, task_id):
"""任务状态查询视图"""
try:
task_result = AsyncResult(task_id)
response_data = {
'task_id': task_id,
'status': task_result.status,
}
if task_result.status == 'SUCCESS':
response_data['result'] = task_result.result
elif task_result.status == 'FAILURE':
response_data['error'] = str(task_result.result)
elif task_result.status == 'PROGRESS':
response_data['progress'] = task_result.result
return JsonResponse(response_data)
except Exception as e:
return JsonResponse({
'status': 'error',
'message': f'Failed to get task status: {str(e)}'
}, status=500)
7. 监控与管理
7.1 使用Flower监控Celery
Flower是Celery的实时监控工具,提供Web界面:
python
# 启动Flower监控
celery -A celery_app flower --port=5555
# 带认证的启动
celery -A celery_app flower --port=5555 --basic_auth=admin:password
Flower提供的功能:
- 实时任务监控
- Worker状态查看
- 任务历史记录
- 任务速率统计
- API接口
7.2 自定义监控指标
实现自定义监控和指标收集:
python
# tasks/monitoring_tasks.py
from celery import Celery
from celery_app import app
import time
import logging
from datetime import datetime, timedelta
from prometheus_client import Counter, Histogram, Gauge, generate_latest
import redis
logger = logging.getLogger(__name__)
# 定义监控指标
TASK_STARTED_COUNTER = Counter('celery_task_started_total', 'Total started tasks', ['task_name'])
TASK_COMPLETED_COUNTER = Counter('celery_task_completed_total', 'Total completed tasks', ['task_name', 'status'])
TASK_DURATION_HISTOGRAM = Histogram('celery_task_duration_seconds', 'Task duration in seconds', ['task_name'])
ACTIVE_TASKS_GAUGE = Gauge('celery_active_tasks', 'Currently active tasks', ['task_name'])
# Redis连接用于存储统计信息
redis_client = redis.Redis(host='localhost', port=6379, db=2)
@app.task(bind=True)
def monitored_task(self, *args, **kwargs):
"""
带有监控的任务装饰器
"""
task_name = self.name
start_time = time.time()
# 更新指标
TASK_STARTED_COUNTER.labels(task_name=task_name).inc()
ACTIVE_TASKS_GAUGE.labels(task_name=task_name).inc()
try:
# 记录任务开始时间
task_key = f"task:{self.request.id}"
redis_client.hset(task_key, mapping={
'name': task_name,
'start_time': start_time,
'status': 'started',
'args': str(args),
'kwargs': str(kwargs)
})
# 执行实际任务逻辑
result = self.run(*args, **kwargs)
# 记录成功
end_time = time.time()
duration = end_time - start_time
TASK_COMPLETED_COUNTER.labels(task_name=task_name, status='success').inc()
TASK_DURATION_HISTOGRAM.labels(task_name=task_name).observe(duration)
ACTIVE_TASKS_GAUGE.labels(task_name=task_name).dec()
# 更新Redis记录
redis_client.hset(task_key, mapping={
'end_time': end_time,
'duration': duration,
'status': 'success',
'result': str(result)
})
# 设置过期时间(24小时)
redis_client.expire(task_key, 24 * 3600)
return result
except Exception as e:
# 记录失败
end_time = time.time()
duration = end_time - start_time
TASK_COMPLETED_COUNTER.labels(task_name=task_name, status='failure').inc()
TASK_DURATION_HISTOGRAM.labels(task_name=task_name).observe(duration)
ACTIVE_TASKS_GAUGE.labels(task_name=task_name).dec()
# 更新Redis记录
redis_client.hset(task_key, mapping={
'end_time': end_time,
'duration': duration,
'status': 'failure',
'error': str(e)
})
redis_client.expire(task_key, 24 * 3600)
raise
@app.task
def collect_celery_metrics():
"""
收集Celery指标任务
"""
try:
# 获取任务统计
inspect = app.control.inspect()
# 活跃任务
active_tasks = inspect.active()
active_count = sum(len(tasks) for tasks in (active_tasks or {}).values())
# 预定任务
scheduled_tasks = inspect.scheduled()
scheduled_count = sum(len(tasks) for tasks in (scheduled_tasks or {}).values())
# 注册任务
registered_tasks = inspect.registered()
task_count = sum(len(tasks) for tasks in (registered_tasks or {}).values())
# 存储到Redis
metrics_key = "celery:metrics"
metrics_data = {
'active_tasks': active_count,
'scheduled_tasks': scheduled_count,
'registered_tasks': task_count,
'timestamp': datetime.now().isoformat()
}
redis_client.hset(metrics_key, mapping=metrics_data)
logger.info(f"指标收集完成: {metrics_data}")
return metrics_data
except Exception as e:
logger.error(f"指标收集失败: {str(e)}")
return {'status': 'error', 'message': str(e)}
@app.task
def cleanup_old_tasks(days=7):
"""
清理旧任务记录
"""
try:
# 查找过期的任务键
pattern = "task:*"
keys = redis_client.keys(pattern)
deleted_count = 0
for key in keys:
task_data = redis_client.hgetall(key)
if b'start_time' in task_data:
start_time = float(task_data[b'start_time'])
task_age = time.time() - start_time
# 删除超过指定天数的记录
if task_age > days * 24 * 3600:
redis_client.delete(key)
deleted_count += 1
logger.info(f"清理了 {deleted_count} 个旧任务记录")
return {'deleted_count': deleted_count, 'days': days}
except Exception as e:
logger.error(f"清理任务失败: {str(e)}")
return {'status': 'error', 'message': str(e)}
8. 性能优化与最佳实践
8.1 配置优化
生产环境下的Celery配置优化:
python
# celery_production.py
import os
from celery import Celery
from celery.schedules import crontab
# 生产环境配置
class ProductionConfig:
# Broker设置
broker_url = 'redis://localhost:6379/0'
result_backend = 'redis://localhost:6379/1'
# 任务序列化
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
# 时区设置
timezone = 'Asia/Shanghai'
enable_utc = True
# Worker设置
worker_prefetch_multiplier = 1 # 生产环境建议设为1
worker_max_tasks_per_child = 100 # 防止内存泄漏
worker_disable_rate_limits = False
# 任务执行设置
task_acks_late = True # 任务完成后确认
task_reject_on_worker_lost = True # Worker丢失时拒绝任务
task_always_eager = False # 生产环境必须为False
# 结果设置
result_expires = 3600 # 1小时
result_compression = 'gzip'
# 队列路由
task_routes = {
'tasks.email_tasks.*': {'queue': 'email'},
'tasks.file_tasks.*': {'queue': 'files'},
'tasks.analytics_tasks.*': {'queue': 'analytics'},
'tasks.monitoring_tasks.*': {'queue': 'monitoring'},
}
# 定时任务
beat_schedule = {
'collect-metrics-every-5-minutes': {
'task': 'tasks.monitoring_tasks.collect_celery_metrics',
'schedule': 300.0, # 每5分钟
},
'cleanup-old-tasks-daily': {
'task': 'tasks.monitoring_tasks.cleanup_old_tasks',
'schedule': crontab(hour=2, minute=0), # 每天凌晨2点
},
'send-daily-report': {
'task': 'tasks.email_tasks.send_daily_report',
'schedule': crontab(hour=9, minute=0), # 每天上午9点
},
}
# 安全设置
worker_hijack_root_logger = False
worker_redirect_stdouts = False
# 创建应用
app = Celery('myproject')
app.config_from_object(ProductionConfig)
# 自动发现任务
app.autodiscover_tasks(['tasks'])
8.2 部署配置
使用Supervisor管理Celery进程:
ini
; /etc/supervisor/conf.d/celery.conf
; Celery Worker进程
[program:celery_worker]
command=/path/to/venv/bin/celery -A celery_production worker --loglevel=info --concurrency=4
directory=/path/to/your/project
user=www-data
numprocs=1
stdout_logfile=/var/log/celery/worker.log
stderr_logfile=/var/log/celery/worker.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=600
; Celery Beat进程
[program:celery_beat]
command=/path/to/venv/bin/celery -A celery_production beat --loglevel=info
directory=/path/to/your/project
user=www-data
numprocs=1
stdout_logfile=/var/log/celery/beat.log
stderr_logfile=/var/log/celery/beat.log
autostart=true
autorestart=true
startsecs=10
9. 完整代码示例
9.1 项目结构
myproject/
├── app.py # Flask应用
├── celery_app.py # Celery应用配置
├── celery_production.py # 生产环境配置
├── requirements.txt # 依赖包
├── tasks/ # 任务模块
│ ├── __init__.py
│ ├── email_tasks.py
│ ├── file_tasks.py
│ ├── advanced_tasks.py
│ ├── workflow_tasks.py
│ └── monitoring_tasks.py
├── templates/ # Flask模板
│ └── index.html
└── uploads/ # 文件上传目录
9.2 完整的Celery应用配置
python
# celery_app.py (完整版本)
import os
import logging
from celery import Celery
from celery.signals import after_setup_logger, after_setup_task_logger
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 创建Celery应用
app = Celery(
'myproject',
broker='redis://localhost:6379/0',
backend='redis://localhost:6379/1',
include=[
'tasks.email_tasks',
'tasks.file_tasks',
'tasks.advanced_tasks',
'tasks.workflow_tasks',
'tasks.monitoring_tasks'
]
)
# 基础配置
app.conf.update(
# 序列化配置
task_serializer='json',
result_serializer='json',
accept_content=['json'],
timezone='Asia/Shanghai',
enable_utc=True,
# 任务路由
task_routes={
'tasks.email_tasks.*': {'queue': 'email'},
'tasks.file_tasks.*': {'queue': 'files'},
'tasks.advanced_tasks.*': {'queue': 'api'},
'tasks.workflow_tasks.*': {'queue': 'workflow'},
'tasks.monitoring_tasks.*': {'queue': 'monitoring'},
},
# 任务结果
result_expires=3600,
# Worker配置
worker_prefetch_multiplier=4,
worker_max_tasks_per_child=1000,
# 任务确认
task_acks_late=True,
task_reject_on_worker_lost=True,
)
# 日志配置信号处理器
@after_setup_logger.connect
def setup_logger(logger, *args, **kwargs):
"""配置Celery日志"""
logger.setLevel(logging.INFO)
@after_setup_task_logger.connect
def setup_task_logger(logger, *args, **kwargs):
"""配置任务日志"""
logger.setLevel(logging.INFO)
if __name__ == '__main__':
app.start()
9.3 依赖文件
txt
# requirements.txt
celery==5.3.4
redis==4.6.0
flower==1.2.0
flask==2.3.3
pandas==2.0.3
requests==2.31.0
prometheus-client==0.17.1
openpyxl==3.1.2
10. 总结
10.1 核心优势
通过本文的全面介绍,我们可以看到Celery在Python Web应用异步任务处理中的核心优势:
- 提升用户体验:将耗时操作异步化,避免用户长时间等待
- 提高系统吞吐量:通过任务队列平衡负载,提高系统处理能力
- 增强系统可靠性:任务重试、错误处理和降级机制
- 灵活的扩展性:支持分布式部署和水平扩展
- 完善的生态系统:丰富的监控工具和第三方集成
10.2 性能指标对比
使用Celery前后系统性能的对比可以用以下公式表示:
性能提升 = T sync − T async T sync × 100 % \text{性能提升} = \frac{T_{\text{sync}} - T_{\text{async}}}{T_{\text{sync}}} \times 100\% 性能提升=TsyncTsync−Tasync×100%
其中:
- T sync T_{\text{sync}} Tsync 是同步处理时间
- T async T_{\text{async}} Tasync 是异步处理时间
在实际应用中,对于耗时操作,性能提升通常可以达到80%以上。
10.3 最佳实践总结
-
任务设计原则
- 任务应该是幂等的
- 合理设置任务超时和重试策略
- 使用任务状态跟踪和进度报告
-
部署运维
- 使用进程管理工具(如Supervisor)
- 配置完善的监控和告警
- 定期清理任务结果和日志
-
性能优化
- 根据任务特性配置合适的并发数
- 使用任务路由实现负载均衡
- 合理配置消息代理和结果后端
-
错误处理
- 实现完善的异常处理机制
- 使用熔断器和降级策略
- 记录详细的错误日志
通过合理使用Celery,我们可以构建出高性能、高可用的Web应用系统,为用户提供更好的服务体验。