Python中如何使用Django + Celery实现定时任务

请问我现在想用 django + celery 做定时任务从外部拉取数据后写库。

但是如果我的项目为了做负载均衡同时部署在两台服务器上,那么定时任务就会执行两次。

这种情况应该怎么避免?


Python中如何使用Django + Celery实现定时任务
13 回复

celery 只在一台服务器上跑就好了


Django + Celery 定时任务配置指南

要在Django里用Celery搞定时任务,得先装几个包:

pip install celery django-celery-beat redis

1. 基础配置

在settings.py里加上这些配置:

# Celery配置
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'

2. 创建Celery实例

在项目目录下建个celery.py

import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project.settings')
app = Celery('your_project')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

然后在__init__.py里导入:

from .celery import app as celery_app
__all__ = ('celery_app',)

3. 写任务函数

在某个app的tasks.py里:

from celery import shared_task
from django.utils import timezone

@shared_task
def send_daily_report():
    print(f"每日报告发送时间:{timezone.now()}")
    # 这里写你的业务逻辑

@shared_task
def cleanup_old_data():
    print("清理过期数据任务执行")
    # 清理逻辑

4. 配置定时计划

两种方式设置计划:

方式A:代码配置(settings.py

CELERY_BEAT_SCHEDULE = {
    'daily-report': {
        'task': 'your_app.tasks.send_daily_report',
        'schedule': 86400.0,  # 每天执行
    },
    'hourly-cleanup': {
        'task': 'your_app.tasks.cleanup_old_data',
        'schedule': 3600.0,  # 每小时
    },
}

方式B:数据库配置(推荐)

# 先迁移数据库
python manage.py migrate django_celery_beat

# 然后在代码里创建计划
from django_celery_beat.models import PeriodicTask, IntervalSchedule

# 创建间隔计划(每30分钟)
schedule, _ = IntervalSchedule.objects.get_or_create(
    every=30,
    period=IntervalSchedule.MINUTES,
)

# 创建定时任务
PeriodicTask.objects.create(
    interval=schedule,
    name='每30分钟任务',
    task='your_app.tasks.some_task',
)

5. 启动服务

开三个终端分别运行:

# 启动Redis
redis-server

# 启动Celery worker
celery -A your_project worker --loglevel=info

# 启动Celery beat(定时任务调度器)
celery -A your_project beat --loglevel=info

6. 动态管理任务

用数据库方式的话,可以在Django admin里管理任务:

# admin.py
from django.contrib import admin
from django_celery_beat.models import PeriodicTask

admin.site.register(PeriodicTask)

这样就搞定了。用数据库存储计划的好处是能在运行时修改任务,不用重启服务。记得worker和beat要一直运行着。

总结:装包、配Redis、写任务、设计划、启服务。

celery 不是可以分布式的么, 为啥会执行两次呀
你是不是将你的项目完完全全复制了一份 用了两个 celery

celery 不是和 django 集成在一起的吗?怎么单独放到一台服务器上?
对,我其实就是在两台服务器上整体部署了两次

是说 celery 的 beat 和 work 只在一台服务器上启动就行吗

beat 启动后,会将任务丢到 redis 中, 很多机器上中的某一个 worker 会执行这个任务
只要 url 一样就可以实现,我单独写 celery 是这样的 django-celery 应该差不多

只需一个 beat,worker 可以多个

celery 定时任务的启动不是由 django 发起的,是由 beat 发起的?我还以为定时任务是由 Django 发起丢给 beat 然后再分配执行的呢。

但是 django 不是直接连 redis 或 rabbitmq 的吗?难道定时任务的发起不是 django->redis->beat 这个流程吗

celery 怎么会是和 Django 一起呢,celery 应该都是单独部署的吧

关于定时任务这个由谁发起的,我不能定论.
但是 django 中有两种写定时任务的方法,一种是 settings 中直接配置(我觉得你是这种),还有一种是动态添加
<br>CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'<br>
所有的定时任务都是又这个 Scheduler 调度的,添加任务删除任务的时候,这个 Scheduler 都能自动更新(其实就是读表,可以在数据库找到表 crontab,xxx 等表)
这个 Scheduler 将任务丢到 redis 中,worker 就知道有任务了,就执行了,其实跟 django 我觉得没任何关系

我好像明白了。我之前因为在启动 celery 时要指定 django app 所以误以为是由 django 做的任务发起。其实这个只是为了获取相关配置。

我大概知道你说的意思,如你所说,两个机器的任务其实是互斥的。
其实你这里的逻辑就有问题。
如果你是为了做分布式,那么两台机器都做定时任务本来就是正确的啊,为什么会互斥呢?
如果你说的是同一个任务,不应该被重复执行两次,那么你应该引入一个队列弹出的机制
总之你应该理顺一下思路,我感觉你是有点模糊的。一楼答案就说的很好,如果不想两个一起跑,你只跑一个机器就行了啊

回到顶部