整体步骤:
1. 安装django-apscheduler
2. 添加到INSTALLED_APPS
3. 运行迁移,创建数据库表
4. 创建任务函数
5. 在apps.py中启动调度器,避免多实例重复运行
6. 配置调度器,添加定时任务
7. 运行服务器并测试
需要验证每个步骤是否正确,特别是调度器的启动位置,是否在Django应用加载时正确初始化,以及任务是否被正确调度。同时提醒用户注意可能的问题,比如时区设置,任务执行时间等。以下是使用 django_apscheduler
的完整示例,涵盖安装、配置、任务定义、调度及运行步骤:
bash
pip install django-apscheduler
settings.py
python
INSTALLED_APPS = [
# ...
'django_apscheduler', # 添加此应用
]
# 时区配置(可选,但建议明确)
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = True
bash
python manage.py migrate
jobs.py
python
# myapp/jobs.py
from django_apscheduler.jobstores import DjangoJobStore
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
from django.conf import settings
def my_scheduled_job():
"""示例任务:每分钟打印日志"""
print("[APScheduler] 定时任务执行成功!")
def start_scheduler():
"""启动调度器"""
scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
scheduler.add_jobstore(DjangoJobStore(), "default")
# 添加任务(每分钟执行一次)
scheduler.add_job(
my_scheduled_job,
trigger=CronTrigger(second="0"), # 每分钟的第0秒触发
id="my_job",
max_instances=1,
replace_existing=True,
)
# 启动调度器
try:
scheduler.start()
except KeyboardInterrupt:
scheduler.shutdown()
apps.py
(确保调度器只启动一次)python
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
"""Django 启动时自动初始化调度器"""
if not settings.DEBUG: # 生产环境启动,或根据需求调整
from .jobs import start_scheduler
start_scheduler()
__init__.py
python
# myapp/__init__.py
default_app_config = 'myapp.apps.MyAppConfig'
bash
python manage.py runserver
plaintext
[APScheduler] 定时任务执行成功! # 每分钟输出一次
django_apscheduler
自动注册模型到 Admin,访问 /admin
可查看任务状态:
python
# 在代码中动态添加一次性任务
from apscheduler.triggers.date import DateTrigger
from datetime import datetime, timedelta
def add_one_time_job():
scheduler = BackgroundScheduler()
scheduler.add_job(
my_scheduled_job,
trigger=DateTrigger(run_date=datetime.now() + timedelta(minutes=5)),
id="one_time_job",
)
若部署多个 Django 实例,需通过数据库锁避免任务重复执行:
python
复制
# 在 jobs.py 的 start_scheduler 中添加锁机制 from django_apscheduler.models import DjangoJobExecution DjangoJobExecution.objects.select_for_update().filter(job_id="my_job").exists()
myproject/ ├── myproject/ │ ├── settings.py │ └── urls.py └── myapp/ ├── __init__.py ├── apps.py ├── jobs.py ├── models.py └── admin.py
开发环境:在 DEBUG=True
时可能因代码重载导致调度器多次启动,建议通过 if not settings.DEBUG
控制。
持久化:任务状态存储在数据库表 django_apscheduler_djangojob
和 django_apscheduler_djangojobexecution
中。
时区:确保 TIME_ZONE
配置一致,避免任务时间偏移。
通过以上步骤,你可以在 Django 中快速实现轻量级定时任务调度!
在 Django 项目中,如果存在多个应用(Apps),使用 django_apscheduler
需要合理组织代码以避免任务冲突或重复执行。以下是详细的解决方案:
避免多次启动调度器:确保全局只有一个调度器实例(BackgroundScheduler
),否则会引发线程冲突。
集中管理任务注册:推荐将所有任务的注册逻辑集中在一个位置(如某个 App 或中间件中),而非分散到多个 App 的 ready()
方法中。
任务 ID 唯一性:不同 App 中的任务需要唯一标识符(id
参数),避免覆盖或冲突。
scheduler
)bash
python manage.py startapp scheduler
scheduler
中定义全局调度器python
# scheduler/jobs.py
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore
from django.conf import settings
# 全局调度器实例
scheduler = None
def init_scheduler():
global scheduler
if not scheduler:
scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
scheduler.add_jobstore(DjangoJobStore(), "default")
return scheduler
app1
和 app2
为例)python
# app1/tasks.py
def app1_task():
print("App1 定时任务执行")
# app2/tasks.py
def app2_task():
print("App2 定时任务执行")
scheduler
中统一注册所有任务python
# scheduler/jobs.py(续)
from app1.tasks import app1_task
from app2.tasks import app2_task
def register_jobs():
scheduler = init_scheduler()
# 注册 app1 的任务(每天凌晨执行)
scheduler.add_job(
app1_task,
trigger="cron", hour=0, minute=0,
id="app1_daily_task",
replace_existing=True # 避免重复注册
)
# 注册 app2 的任务(每小时执行)
scheduler.add_job(
app2_task,
trigger="interval", hours=1,
id="app2_hourly_task",
replace_existing=True
)
return scheduler
scheduler
的 AppConfig 中启动调度器python
# scheduler/apps.py
from django.apps import AppConfig
from django.conf import settings
class SchedulerConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'scheduler'
def ready(self):
if not settings.DEBUG: # 生产环境启动
from .jobs import init_scheduler, register_jobs
scheduler = init_scheduler()
register_jobs()
try:
scheduler.start()
except KeyboardInterrupt:
scheduler.shutdown()
settings.py
python
INSTALLED_APPS = [
# ...
'django_apscheduler',
'app1',
'app2',
'scheduler', # 确保最后加载,以便其他 App 的任务已定义
]
如果希望每个 App 管理自己的任务,需确保调度器仅初始化一次:
在公共模块中定义调度器
python
# core/scheduler.py
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore
from django.conf import settings
scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
scheduler.add_jobstore(DjangoJobStore(), "default")
def get_scheduler():
return scheduler
在每个 App 的 ready()
中注册任务
python
# app1/apps.py
from django.apps import AppConfig
from core.scheduler import get_scheduler
class App1Config(AppConfig):
def ready(self):
scheduler = get_scheduler()
if not scheduler.running:
scheduler.start()
# 注册任务
scheduler.add_job(
self.my_task,
trigger="interval", minutes=30,
id="app1_task",
replace_existing=True
)
def my_task(self):
print("App1 任务执行")
风险提示
需确保 scheduler.start()
只调用一次。
多个 App 的 ready()
加载顺序可能导致竞争条件。
若项目部署多个实例(如多台服务器或多容器),需通过数据库锁避免重复执行:
python
# scheduler/jobs.py(续)
from django_apscheduler.models import DjangoJobExecution
def register_jobs():
scheduler = init_scheduler()
# 注册任务时检查锁
with transaction.atomic():
job, created = DjangoJobExecution.objects.select_for_update().get_or_create(
job_id="app1_daily_task",
defaults={"status": "idle"}
)
if created:
scheduler.add_job(...)
myproject/ ├── core/ │ └── scheduler.py # 全局调度器定义(可选方案) ├── app1/ │ ├── tasks.py # 定义 app1 的任务 │ ├── apps.py ├── app2/ │ ├── tasks.py # 定义 app2 的任务 │ ├── apps.py ├── scheduler/ # 专用调度器 App │ ├── jobs.py # 注册所有任务 │ ├── apps.py ├── myproject/ │ ├── settings.py │ └── urls.py
6. 注意事项
避免 DEBUG 模式重复启动:在 settings.DEBUG=True
时,Django 可能会因代码重载多次初始化调度器,建议通过环境变量或 AppConfig
条件判断控制。
任务 ID 唯一性:跨 App 的任务必须使用唯一 id
,否则后注册的任务会覆盖先前的。
数据库清理:定期清理 DjangoJobExecution
表,避免历史记录堆积:
bash
python manage.py delete_old_job_executions 7 # 删除7天前的记录
多 App 场景推荐集中式管理:通过一个专用 App(如 scheduler
)统一注册任务,确保调度器单例。
动态任务需谨慎:若需动态添加任务(如通过 API),需保证线程安全和任务 ID 唯一性。
生产环境高可用:多实例部署时结合数据库锁或外部协调服务(如 Redis 分布式锁)。