基于Django和APScheduler的轻量级异步任务调度系统
摘要
在现代Web应用开发中,任务调度是一个至关重要的功能。无论是定时数据备份、周期性数据同步、还是延时通知发送,都需要一个可靠的任务调度系统。本文介绍了一个基于Django和APScheduler构建的轻量级异步任务调度系统,该系统不仅支持多种调度方式,还提供了完整的Web管理界面和REST API接口。
1. 系统概述
1.1 项目背景
传统的定时任务通常依赖系统级别的cron或Windows任务计划程序,这些方案存在以下问题:
- 管理复杂:需要直接操作系统配置
- 监控困难:缺乏统一的监控和日志系统
- 扩展性差:难以与Web应用集成
- 维护成本高:分散的配置增加了运维复杂度
为了解决这些问题,我们开发了这个基于Python生态的任务调度系统,它具有以下优势:
- 与Django框架深度集成
- 提供直观的Web管理界面
- 支持RESTful API操作
- 内置完善的监控和日志功能
1.2 技术选型
- Django 4.2.16:Web框架,提供ORM、Admin界面等
- APScheduler 3.10.4:Python任务调度库,支持多种触发器
- Django REST Framework:RESTful API支持
- SQLAlchemy 2.0.23:数据库操作,与APScheduler集成
- SQLite:轻量级数据库,便于部署
2. 系统架构设计
2.1 整体架构
css
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Web前端/API │◄──►│ Django应用 │◄──►│ APScheduler │
│ │ │ │ │ 调度器核心 │
│ - 管理界面 │ │ - 任务管理 │ │ │
│ - REST API │ │ - 用户认证 │ │ - 任务执行 │
│ - 任务监控 │ │ - 权限控制 │ │ - 触发器管理 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└────────────────────────┼────────────────────────┘
▼
┌─────────────────┐
│ 数据持久层 │
│ │
│ - 任务定义 │
│ - 执行日志 │
│ - 统计数据 │
└─────────────────┘
2.2 核心组件
2.2.1 任务执行器 (AsyncTaskExecutor)
python
class AsyncTaskExecutor:
"""异步任务执行器 - 单例模式"""
_instance = None
_executor = None
_tasks = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._tasks = {}
cls._init_executor()
return cls._instance
def submit_task(self, strategy_id, task_func, *args):
"""提交任务到线程池"""
log = TaskExecutionLog.objects.create(
strategy_id=strategy_id,
status=TaskExecutionLog.StatusChoices.PENDING
)
if asyncio.iscoroutinefunction(task_func):
# 异步任务处理
future = self._executor.submit(
self._run_async_task_wrapper,
task_func, log.id, *args
)
else:
# 同步任务处理
future = self._executor.submit(
self._run_task, task_func, log.id, *args
)
return log.id
2.2.2 调度器管理器 (SchedulerManager)
python
class SchedulerManager:
"""APScheduler调度器管理器"""
def start_scheduler(self):
"""启动调度器"""
if self._scheduler is None:
config = getattr(settings, 'SCHEDULER_CONFIG', {})
self._scheduler = BackgroundScheduler()
if config:
self._scheduler.configure(**config)
self._add_event_listeners()
if not self._scheduler.running:
self._scheduler.start()
self._is_running = True
self._load_active_tasks()
def add_job_from_task(self, task: ScheduledTask) -> bool:
"""从数据库任务模型添加到调度器"""
try:
module = importlib.import_module(task.module_path)
func = getattr(module, task.function_name)
trigger = self._create_trigger(task.trigger_type, task.trigger_config)
job = self._scheduler.add_job(
func=self._job_wrapper,
trigger=trigger,
args=[task.id, func] + list(task.args),
kwargs=task.kwargs,
id=task.job_id,
max_instances=task.max_instances,
replace_existing=True,
name=task.name
)
task.next_run_time = job.next_run_time
task.save()
return True
except Exception as e:
logger.error(f"添加任务失败: {e}")
return False
2.2.3 任务服务层 (TaskService)
python
class TaskService:
"""任务管理服务 - 业务逻辑层"""
def create_scheduled_task(self, name, module_path, function_name,
trigger_type, trigger_config, **kwargs):
"""创建调度任务"""
job_id = f"task_{uuid.uuid4().hex[:8]}_{int(timezone.now().timestamp())}"
with transaction.atomic():
task = ScheduledTask.objects.create(
name=name,
module_path=module_path,
function_name=function_name,
trigger_type=trigger_type,
trigger_config=trigger_config,
job_id=job_id,
**kwargs
)
TaskMetrics.objects.create(scheduled_task=task)
if task.is_active and self.scheduler_manager.is_running:
self.scheduler_manager.add_job_from_task(task)
return task
def create_interval_task(self, name, module_path, function_name,
seconds=0, minutes=0, hours=0, days=0, **kwargs):
"""创建间隔执行任务的便捷方法"""
trigger_config = {
'seconds': seconds, 'minutes': minutes,
'hours': hours, 'days': days
}
return self.create_scheduled_task(
name=name, module_path=module_path,
function_name=function_name,
trigger_type='interval',
trigger_config=trigger_config,
**kwargs
)
3. 数据模型设计
3.1 核心模型
3.1.1 调度任务模型
python
class ScheduledTask(models.Model):
"""调度任务模型"""
name = models.CharField(max_length=255, verbose_name="任务名称")
description = models.TextField(blank=True, verbose_name="任务描述")
# 任务函数信息
module_path = models.CharField(max_length=500, verbose_name="模块路径")
function_name = models.CharField(max_length=255, verbose_name="函数名称")
args = models.JSONField(default=list, verbose_name="位置参数")
kwargs = models.JSONField(default=dict, verbose_name="关键字参数")
# 调度配置
trigger_type = models.CharField(
max_length=20,
choices=[
('interval', '间隔执行'),
('cron', 'Cron表达式'),
('date', '定时执行'),
],
verbose_name="触发器类型"
)
trigger_config = models.JSONField(default=dict, verbose_name="触发器配置")
# 状态管理
is_active = models.BooleanField(default=True, verbose_name="是否启用")
max_instances = models.IntegerField(default=1, verbose_name="最大实例数")
# APScheduler job_id
job_id = models.CharField(max_length=255, unique=True, verbose_name="任务ID")
3.1.2 执行日志模型
python
class TaskExecutionLog(models.Model):
"""任务执行日志"""
class StatusChoices(models.TextChoices):
PENDING = 'pending', '等待中'
RUNNING = 'running', '运行中'
COMPLETED = 'completed', '已完成'
FAILED = 'failed', '失败'
CANCELLED = 'cancelled', '已取消'
scheduled_task = models.ForeignKey(
ScheduledTask, on_delete=models.CASCADE,
related_name='execution_logs'
)
status = models.CharField(
max_length=20, choices=StatusChoices.choices,
default=StatusChoices.PENDING
)
start_time = models.DateTimeField(auto_now_add=True)
end_time = models.DateTimeField(null=True, blank=True)
result = models.JSONField(null=True, blank=True)
error_message = models.TextField(null=True, blank=True)
execution_time = models.FloatField(null=True, blank=True)
4. 功能特性详解
4.1 多种触发器支持
4.1.1 间隔触发器 (Interval Trigger)
适用于需要固定间隔执行的任务:
python
# 每30秒执行一次
task_service.create_interval_task(
name="系统监控任务",
module_path="monitoring_tasks",
function_name="check_system_health",
seconds=30
)
# 每5分钟执行一次
task_service.create_interval_task(
name="数据同步任务",
module_path="sync_tasks",
function_name="sync_user_data",
minutes=5
)
4.1.2 Cron触发器 (Cron Trigger)
适用于需要在特定时间执行的任务:
python
# 每天凌晨2点执行备份
task_service.create_cron_task(
name="数据库备份",
module_path="backup_tasks",
function_name="backup_database",
hour=2, minute=0
)
# 每周一早上9点发送周报
task_service.create_cron_task(
name="周报生成",
module_path="report_tasks",
function_name="generate_weekly_report",
day_of_week=1, hour=9, minute=0
)
4.1.3 日期触发器 (Date Trigger)
适用于一次性定时任务:
python
from datetime import datetime, timedelta
# 1小时后执行一次性任务
run_time = datetime.now() + timedelta(hours=1)
task_service.create_date_task(
name="延时通知",
module_path="notification_tasks",
function_name="send_delayed_notification",
run_date=run_time,
args=["用户ID123", "重要提醒"]
)
4.2 异步任务支持
系统支持同步和异步两种任务函数:
4.2.1 同步任务示例
python
def sync_user_data():
"""同步用户数据 - 同步函数"""
logger.info("开始同步用户数据")
# 模拟数据处理
time.sleep(2)
result = "同步完成,处理了1000条记录"
logger.info(result)
return result
4.2.2 异步任务示例
python
async def async_fetch_data():
"""异步获取外部数据"""
logger.info("开始异步获取数据")
async with aiohttp.ClientSession() as session:
async with session.get('https://api.example.com/data') as response:
data = await response.json()
logger.info("数据获取完成")
return data
4.3 REST API接口
系统提供完整的RESTful API:
4.3.1 任务管理API
bash
# 创建任务
POST /api/tasks/
{
"name": "定时清理任务",
"description": "每天清理临时文件",
"module_path": "cleanup_tasks",
"function_name": "cleanup_temp_files",
"trigger_type": "cron",
"trigger_config": {"hour": 3, "minute": 0},
"is_active": true
}
# 获取任务列表
GET /api/tasks/?is_active=true&trigger_type=interval
# 控制任务
POST /api/tasks/1/control/
{
"action": "pause" # pause, resume, run_now, delete
}
# 获取任务执行日志
GET /api/tasks/1/logs/?limit=20
4.3.2 异步任务API
bash
# 提交异步任务
POST /api/async-tasks/submit/
{
"strategy_id": "data_process_001",
"module_path": "data_tasks",
"function_name": "process_large_dataset",
"args": ["/path/to/data.csv"],
"kwargs": {"batch_size": 1000}
}
# 查询任务状态
GET /api/async-tasks/12345/status/
4.3.3 调度器管理API
bash
# 获取调度器状态
GET /api/scheduler/status/
# 启动/停止调度器
POST /api/scheduler/start/
POST /api/scheduler/stop/
# 获取所有运行中的任务
GET /api/scheduler/jobs/
5. 实际应用示例
5.1 电商系统应用
5.1.1 订单处理任务
python
def process_pending_orders():
"""处理待付款订单"""
from ecommerce.models import Order
# 查找超过30分钟未付款的订单
timeout = timezone.now() - timedelta(minutes=30)
pending_orders = Order.objects.filter(
status='pending',
created_at__lt=timeout
)
cancelled_count = 0
for order in pending_orders:
order.status = 'cancelled'
order.save()
# 释放库存
order.release_inventory()
# 发送取消通知
send_cancellation_email(order.user.email, order.id)
cancelled_count += 1
return f"取消了 {cancelled_count} 个超时订单"
# 创建每5分钟执行的订单处理任务
task_service.create_interval_task(
name="订单超时处理",
module_path="ecommerce.tasks",
function_name="process_pending_orders",
minutes=5
)
5.1.2 库存同步任务
python
async def sync_inventory_with_warehouse():
"""与仓库系统同步库存"""
logger.info("开始同步库存数据")
async with aiohttp.ClientSession() as session:
# 获取仓库库存数据
async with session.get(
'https://warehouse-api.company.com/inventory',
headers={'Authorization': 'Bearer ' + WAREHOUSE_TOKEN}
) as response:
warehouse_data = await response.json()
# 更新本地库存
updated_count = 0
for item in warehouse_data['items']:
product_id = item['product_id']
new_stock = item['available_quantity']
try:
product = Product.objects.get(id=product_id)
if product.stock != new_stock:
product.stock = new_stock
product.save()
updated_count += 1
except Product.DoesNotExist:
logger.warning(f"产品 {product_id} 不存在")
result = f"库存同步完成,更新了 {updated_count} 个产品"
logger.info(result)
return result
# 每小时同步一次库存
task_service.create_interval_task(
name="库存同步任务",
module_path="ecommerce.tasks",
function_name="sync_inventory_with_warehouse",
hours=1
)
5.2 内容管理系统应用
5.2.1 内容审核任务
python
def auto_review_pending_content():
"""自动审核待审核内容"""
from cms.models import Article, Comment
from cms.services import ContentModerationService
moderation_service = ContentModerationService()
# 审核文章
pending_articles = Article.objects.filter(status='pending')
for article in pending_articles:
if moderation_service.is_content_safe(article.content):
article.status = 'published'
article.published_at = timezone.now()
article.save()
# 通知作者
notify_author(article.author, article, 'approved')
# 审核评论
pending_comments = Comment.objects.filter(status='pending')
for comment in pending_comments:
if moderation_service.is_content_safe(comment.content):
comment.status = 'approved'
comment.save()
return f"审核完成: {len(pending_articles)} 篇文章, {len(pending_comments)} 条评论"
# 每10分钟执行一次内容审核
task_service.create_interval_task(
name="内容自动审核",
module_path="cms.tasks",
function_name="auto_review_pending_content",
minutes=10
)
5.2.2 SEO优化任务
python
def generate_sitemaps():
"""生成网站地图"""
from django.contrib.sitemaps import GenericSitemap
from cms.models import Article, Category
# 生成文章sitemap
articles = Article.objects.filter(status='published')
article_sitemap = GenericSitemap({
'queryset': articles,
'date_field': 'published_at',
})
# 生成分类sitemap
categories = Category.objects.filter(is_active=True)
category_sitemap = GenericSitemap({
'queryset': categories,
'date_field': 'updated_at',
})
# 写入sitemap文件
sitemap_content = generate_sitemap_xml(article_sitemap, category_sitemap)
with open('static/sitemap.xml', 'w', encoding='utf-8') as f:
f.write(sitemap_content)
# 提交到搜索引擎
submit_sitemap_to_search_engines('https://example.com/sitemap.xml')
return "Sitemap生成并提交完成"
# 每天凌晨4点生成sitemap
task_service.create_cron_task(
name="SEO Sitemap生成",
module_path="cms.tasks",
function_name="generate_sitemaps",
hour=4, minute=0
)
5.3 数据分析系统应用
5.3.1 用户行为分析
python
def analyze_user_behavior():
"""分析用户行为数据"""
from analytics.models import UserAction, UserReport
from datetime import datetime, timedelta
# 分析过去24小时的用户行为
yesterday = timezone.now() - timedelta(days=1)
# 页面访问统计
page_views = UserAction.objects.filter(
action_type='page_view',
created_at__gte=yesterday
).values('page_url').annotate(
view_count=Count('id'),
unique_users=Count('user_id', distinct=True)
).order_by('-view_count')
# 用户活跃度分析
active_users = UserAction.objects.filter(
created_at__gte=yesterday
).values('user_id').distinct().count()
# 转化漏斗分析
conversion_data = analyze_conversion_funnel(yesterday)
# 生成报告
report = UserReport.objects.create(
report_date=yesterday.date(),
page_views=list(page_views),
active_users=active_users,
conversion_data=conversion_data,
generated_at=timezone.now()
)
# 发送日报邮件
send_daily_report_email(report)
return f"用户行为分析完成,活跃用户: {active_users}"
# 每天早上8点生成用户行为报告
task_service.create_cron_task(
name="用户行为分析",
module_path="analytics.tasks",
function_name="analyze_user_behavior",
hour=8, minute=0
)
6. 监控与运维
6.1 任务监控
系统提供多维度的任务监控:
6.1.1 实时状态监控
python
# 获取调度器状态
def get_scheduler_health():
scheduler_manager = SchedulerManager()
status = scheduler_manager.get_scheduler_status()
return {
'scheduler_running': status['running'],
'total_jobs': status['job_count'],
'next_run_time': status['next_run_time'],
'failed_jobs_last_hour': get_failed_jobs_count(hours=1),
'avg_execution_time': get_average_execution_time()
}
6.1.2 性能统计
python
class TaskMetrics(models.Model):
"""任务执行统计"""
scheduled_task = models.OneToOneField(ScheduledTask, on_delete=models.CASCADE)
total_executions = models.IntegerField(default=0)
successful_executions = models.IntegerField(default=0)
failed_executions = models.IntegerField(default=0)
avg_execution_time = models.FloatField(default=0.0)
max_execution_time = models.FloatField(default=0.0)
@property
def success_rate(self):
if self.total_executions == 0:
return 0.0
return (self.successful_executions / self.total_executions) * 100
6.2 日志管理
6.2.1 结构化日志
python
import logging
import json
class TaskLogFormatter(logging.Formatter):
"""任务日志格式化器"""
def format(self, record):
log_entry = {
'timestamp': self.formatTime(record),
'level': record.levelname,
'module': record.module,
'message': record.getMessage(),
}
# 添加任务相关信息
if hasattr(record, 'task_id'):
log_entry['task_id'] = record.task_id
if hasattr(record, 'execution_time'):
log_entry['execution_time'] = record.execution_time
return json.dumps(log_entry, ensure_ascii=False)
6.2.2 日志清理任务
python
def cleanup_old_logs(days_to_keep=30):
"""清理旧的执行日志"""
cutoff_date = timezone.now() - timedelta(days=days_to_keep)
deleted_count = TaskExecutionLog.objects.filter(
start_time__lt=cutoff_date
).delete()[0]
logger.info(f"清理了 {deleted_count} 条 {days_to_keep} 天前的日志")
return deleted_count
# 每周清理一次旧日志
task_service.create_cron_task(
name="日志清理任务",
module_path="maintenance.tasks",
function_name="cleanup_old_logs",
day_of_week=0, hour=2, minute=0, # 每周日凌晨2点
kwargs={'days_to_keep': 30}
)
6.3 错误处理与恢复
6.3.1 任务重试机制
python
def reliable_data_sync(max_retries=3):
"""可靠的数据同步任务"""
for attempt in range(max_retries):
try:
# 执行数据同步逻辑
result = perform_data_sync()
logger.info(f"数据同步成功: {result}")
return result
except Exception as e:
logger.warning(f"数据同步失败 (尝试 {attempt + 1}/{max_retries}): {e}")
if attempt == max_retries - 1:
# 最后一次尝试失败,记录错误并发送告警
logger.error(f"数据同步完全失败: {e}")
send_alert_email("数据同步任务失败", str(e))
raise
# 指数退避重试
time.sleep(2 ** attempt)
6.3.2 健康检查任务
python
def health_check():
"""系统健康检查"""
checks = {
'database': check_database_connection(),
'external_api': check_external_api_availability(),
'disk_space': check_disk_space(),
'memory_usage': check_memory_usage(),
'scheduler': check_scheduler_status()
}
failed_checks = [name for name, status in checks.items() if not status]
if failed_checks:
alert_message = f"健康检查失败: {', '.join(failed_checks)}"
logger.error(alert_message)
send_alert_notification(alert_message)
return {'status': 'unhealthy', 'failed_checks': failed_checks}
return {'status': 'healthy', 'all_checks_passed': True}
# 每5分钟执行健康检查
task_service.create_interval_task(
name="系统健康检查",
module_path="monitoring.tasks",
function_name="health_check",
minutes=5
)
7. 部署与配置
7.1 生产环境配置
7.1.1 数据库配置
python
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'task_scheduler',
'USER': 'scheduler_user',
'PASSWORD': 'secure_password',
'HOST': 'localhost',
'PORT': '5432',
}
}
# APScheduler配置 - 生产环境
SCHEDULER_CONFIG = {
'apscheduler.jobstores.default': {
'type': 'sqlalchemy',
'url': 'postgresql://scheduler_user:secure_password@localhost/task_scheduler'
},
'apscheduler.executors.default': {
'class': 'apscheduler.executors.pool:ThreadPoolExecutor',
'max_workers': 50 # 根据服务器配置调整
},
'apscheduler.job_defaults.coalesce': False,
'apscheduler.job_defaults.max_instances': 3,
'apscheduler.timezone': 'Asia/Shanghai',
}
7.1.2 Redis缓存配置
python
# 使用Redis作为缓存和消息队列
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# Session配置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
7.2 Docker部署
7.2.1 Dockerfile
dockerfile
FROM python:3.11-slim
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y \
gcc \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制项目文件
COPY . .
# 创建非root用户
RUN useradd --create-home --shell /bin/bash scheduler
RUN chown -R scheduler:scheduler /app
USER scheduler
# 暴露端口
EXPOSE 8000
# 启动脚本
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
7.2.2 docker-compose.yml
yaml
version: '3.8'
services:
db:
image: postgres:13
environment:
POSTGRES_DB: task_scheduler
POSTGRES_USER: scheduler_user
POSTGRES_PASSWORD: secure_password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:6-alpine
ports:
- "6379:6379"
web:
build: .
ports:
- "8000:8000"
depends_on:
- db
- redis
environment:
- DEBUG=0
- DATABASE_URL=postgresql://scheduler_user:secure_password@db:5432/task_scheduler
- REDIS_URL=redis://redis:6379/1
volumes:
- ./logs:/app/logs
scheduler:
build: .
command: python manage.py start_scheduler --daemon
depends_on:
- db
- redis
environment:
- DEBUG=0
- DATABASE_URL=postgresql://scheduler_user:secure_password@db:5432/task_scheduler
- REDIS_URL=redis://redis:6379/1
volumes:
- ./logs:/app/logs
volumes:
postgres_data:
8. 性能优化
8.1 数据库优化
8.1.1 索引优化
python
class TaskExecutionLog(models.Model):
# ... 其他字段 ...
class Meta:
indexes = [
models.Index(fields=['status', 'start_time']),
models.Index(fields=['scheduled_task', 'start_time']),
models.Index(fields=['start_time']), # 用于时间范围查询
]
8.1.2 查询优化
python
def get_task_statistics(task_id, days=30):
"""优化的任务统计查询"""
cutoff_date = timezone.now() - timedelta(days=days)
# 使用聚合查询减少数据库访问
stats = TaskExecutionLog.objects.filter(
scheduled_task_id=task_id,
start_time__gte=cutoff_date
).aggregate(
total_count=Count('id'),
success_count=Count('id', filter=Q(status='completed')),
avg_time=Avg('execution_time'),
max_time=Max('execution_time')
)
return {
'success_rate': (stats['success_count'] / stats['total_count'] * 100)
if stats['total_count'] > 0 else 0,
'average_execution_time': stats['avg_time'] or 0,
'max_execution_time': stats['max_time'] or 0
}
8.2 任务执行优化
8.2.1 批处理优化
python
def batch_process_users(batch_size=1000):
"""批量处理用户数据"""
from django.core.paginator import Paginator
users = User.objects.filter(is_active=True)
paginator = Paginator(users, batch_size)
processed_count = 0
for page_num in paginator.page_range:
page = paginator.page(page_num)
# 批量处理当前页的用户
user_ids = [user.id for user in page.object_list]
# 使用bulk_update提高性能
users_to_update = []
for user in page.object_list:
user.last_processed = timezone.now()
users_to_update.append(user)
User.objects.bulk_update(users_to_update, ['last_processed'])
processed_count += len(users_to_update)
# 避免内存泄漏
if processed_count % 10000 == 0:
logger.info(f"已处理 {processed_count} 个用户")
return f"批量处理完成,共处理 {processed_count} 个用户"
8.2.2 异步IO优化
python
import asyncio
import aiohttp
async def fetch_multiple_apis():
"""并发调用多个API"""
urls = [
'https://api1.example.com/data',
'https://api2.example.com/data',
'https://api3.example.com/data'
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_api_data(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
# 处理结果
successful_results = [r for r in results if not isinstance(r, Exception)]
failed_count = len([r for r in results if isinstance(r, Exception)])
return {
'successful_count': len(successful_results),
'failed_count': failed_count,
'data': successful_results
}
async def fetch_api_data(session, url):
"""获取单个API数据"""
try:
async with session.get(url, timeout=30) as response:
return await response.json()
except Exception as e:
logger.error(f"API调用失败 {url}: {e}")
raise
9. 安全考虑
9.1 权限控制
9.1.1 基于角色的访问控制
python
from django.contrib.auth.models import Group, Permission
# 创建角色组
def setup_task_permissions():
# 任务管理员组
admin_group, created = Group.objects.get_or_create(name='Task Administrators')
admin_permissions = Permission.objects.filter(
content_type__app_label='app',
codename__in=['add_scheduledtask', 'change_scheduledtask', 'delete_scheduledtask']
)
admin_group.permissions.set(admin_permissions)
# 任务查看者组
viewer_group, created = Group.objects.get_or_create(name='Task Viewers')
viewer_permissions = Permission.objects.filter(
content_type__app_label='app',
codename='view_scheduledtask'
)
viewer_group.permissions.set(viewer_permissions)
9.1.2 API认证
python
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
class ScheduledTaskViewSet(viewsets.ModelViewSet):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get_permissions(self):
"""根据操作类型设置权限"""
if self.action in ['create', 'update', 'destroy']:
permission_classes = [IsAuthenticated, DjangoModelPermissions]
else:
permission_classes = [IsAuthenticated]
return [permission() for permission in permission_classes]
9.2 任务安全
9.2.1 任务函数白名单
python
# settings.py
ALLOWED_TASK_MODULES = [
'example_tasks',
'business_tasks',
'maintenance_tasks',
'analytics_tasks'
]
def validate_task_function(module_path, function_name):
"""验证任务函数的安全性"""
if module_path not in settings.ALLOWED_TASK_MODULES:
raise ValueError(f"模块 {module_path} 不在允许的模块列表中")
try:
module = importlib.import_module(module_path)
func = getattr(module, function_name)
# 检查函数是否有安全标记
if not getattr(func, '_task_safe', False):
raise ValueError(f"函数 {function_name} 未标记为任务安全")
return func
except (ImportError, AttributeError) as e:
raise ValueError(f"无法导入任务函数: {e}")
# 安全任务装饰器
def task_safe(func):
"""标记函数为任务安全"""
func._task_safe = True
return func
@task_safe
def cleanup_temp_files():
"""清理临时文件 - 安全任务"""
pass
9.2.2 资源限制
python
import resource
import signal
def set_resource_limits():
"""设置任务执行的资源限制"""
# 限制内存使用(1GB)
resource.setrlimit(resource.RLIMIT_AS, (1024*1024*1024, 1024*1024*1024))
# 限制执行时间(30分钟)
def timeout_handler(signum, frame):
raise TimeoutError("任务执行超时")
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(1800) # 30分钟超时
def secure_task_wrapper(task_func, *args, **kwargs):
"""安全的任务包装器"""
try:
set_resource_limits()
result = task_func(*args, **kwargs)
signal.alarm(0) # 取消超时
return result
except Exception as e:
signal.alarm(0)
logger.error(f"任务执行异常: {e}")
raise
10. 总结与展望
10.1 系统优势
- 易于集成:与Django框架深度集成,开发效率高
- 功能完整:支持多种触发器、异步任务、监控统计
- 管理便捷:提供Web界面和REST API
- 监控完善:详细的执行日志和性能统计
- 扩展性强:模块化设计,易于扩展新功能
10.2 适用场景
- 中小型Web应用:需要定时任务但不想引入复杂的消息队列
- 内容管理系统:定时发布、内容审核、SEO优化
- 电商平台:订单处理、库存同步、数据分析
- 企业内部系统:数据备份、报表生成、系统维护
10.3 未来发展方向
10.3.1 分布式支持
python
# 计划添加分布式任务支持
class DistributedTaskManager:
"""分布式任务管理器"""
def __init__(self, redis_client):
self.redis = redis_client
self.node_id = self._generate_node_id()
def acquire_task_lock(self, task_id, timeout=300):
"""获取任务执行锁"""
lock_key = f"task_lock:{task_id}"
return self.redis.set(lock_key, self.node_id, ex=timeout, nx=True)
def release_task_lock(self, task_id):
"""释放任务执行锁"""
lock_key = f"task_lock:{task_id}"
self.redis.delete(lock_key)
10.3.2 可视化监控
计划添加更丰富的监控图表:
- 任务执行时间趋势图
- 成功率统计图表
- 系统资源使用监控
- 实时任务状态大屏
10.3.3 智能调度
python
# 计划添加智能调度功能
class IntelligentScheduler:
"""智能任务调度器"""
def optimize_task_schedule(self, task_history):
"""基于历史数据优化任务调度"""
# 分析任务执行模式
# 预测最佳执行时间
# 自动调整调度策略
pass
def predict_resource_usage(self, tasks):
"""预测资源使用情况"""
# 机器学习预测模型
# 资源使用优化建议
pass
10.4 技术展望
- 云原生支持:Kubernetes部署、自动扩缩容
- 微服务架构:拆分为独立的任务调度服务
- AI集成:智能任务优化和故障预测
- 更好的可观测性:集成Prometheus、Grafana等监控工具
结语
本文详细介绍了基于Django和APScheduler构建的轻量级异步任务调度系统。该系统在保持简单易用的同时,提供了丰富的功能和良好的扩展性。通过实际的应用示例,展示了系统在不同业务场景下的使用方法。
这个任务调度系统特别适合中小型项目,可以大大简化定时任务的开发和管理工作。随着业务的发展,系统也可以逐步扩展为更复杂的分布式任务调度平台。
希望这个系统能够为Python Web开发者提供一个实用的任务调度解决方案,提高开发效率,降低运维成本。