芹菜任务计划与抵消相同

时间:2017-01-13 08:44:31

标签: python django celery crontab schedule

我有几个任务,如下:

CELERYBEAT_SCHEDULE = {
    'task1': {
        'task': 'api.tasks.task1',
        'schedule': timedelta(seconds=10),
    },
    'task2': {
        'task': 'api.tasks.task2',
        'schedule': timedelta(seconds=30),
    },
    'task3': {
        'task': 'api.tasks.task3',
        'schedule': timedelta(seconds=15),
    },
    ...
}

因此,task1将在*:*:10,*:*:20,*:*:30,*:*:40,*:*:50和*:*:00

中运行

task2将在*:*:30和*:*:00

中运行

task3将在*:*:15,*:*:30,*:*:45和*:*:00

中运行

然后任务总是在*:*:30和*:*:00中同意。有没有办法添加偏移量。我想得到这样的东西:

task1(offset = 2)在*:*:12,*:*:22,*:*:32,*:*:42,*:*:52和*:*:02

中运行

task2(offset = 7)在*:*:37和*:*:07

中运行

task3(offset = 0)在*:*:15,*:*:30,*:*:45和*:*:00

中运行

我已经阅读了文档,我想我必须使用crontab,但是不是有另外一种方式更好吗?并且crontab没有秒配置: - (

3 个答案:

答案 0 :(得分:1)

您可以使用以下步骤解决此问题:

1.您不需要在CELERYBEAT_SCHEDULE文件上添加settings.py

2.在__init__.py app中的api文件中添加以下代码:

import tasks

3.然后在tasks.py文件:

from datetime import datetime

from celery import Celery


app = Celery()
run_id = None

@app.task
def task1():
    print('every 10 seconds:', datetime.now().second)

@app.task
def task2():
    print('every 30 seconds:', datetime.now().second)

@app.task
def task3():
    print('every 15 seconds:', datetime.now().second)

@app.task
def run(sender):
    global app, run_id
    # Schedule other tasks
    sender.add_periodic_task(10.0, task1.s())
    sender.add_periodic_task(30.0, task2.s())
    sender.add_periodic_task(15.0, task3.s())
    # Stop self running later times
    app.control.revoke(run_id)

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    global run_id
    now = datetime.now()
    run_id = sender.add_periodic_task((30 if now.second < 30 else 60) - now.second, run.s(sender))

答案 1 :(得分:1)

根据celery documentation

  

您还可以通过扩展自定义自定义计划类型   schedule的界面。

所以这是我的解决方案:

from datetime import timedelta

from celery import Celery
from celery.schedules import schedule


class MySchedule(schedule):
    def __init__(self, run_every=None, offset=None):
        self._run_every = run_every
        self._offset = offset if offset is not None else timedelta(seconds=0)
        self._do_offset = True if self._offset else False
        super(MySchedule, self).__init__(
            run_every=self._run_every + self._offset)

    def is_due(self, last_run_at):
        ret = super(MySchedule, self).is_due(last_run_at)
        if self._do_offset and ret.is_due:
            self._do_offset = False
            self.run_every = self._run_every
            ret = super(MySchedule, self).is_due(last_run_at)
        return ret

    def __reduce__(self):
        return self.__class__, (self._run_every, self._offset)


app = Celery('tasks', broker='pyamqp://guest@localhost//')

app.conf.beat_schedule = {
    'task1': {
        'task': 'tasks.task1',
        'schedule': MySchedule(
            run_every=timedelta(seconds=10), offset=timedelta(seconds=2)),
    },
    'task2': {
        'task': 'tasks.task2',
        'schedule': MySchedule(
            run_every=timedelta(seconds=30), offset=timedelta(seconds=7)),
    },
    'task3': {
        'task': 'tasks.task3',
        'schedule': MySchedule(
            run_every=timedelta(seconds=15), offset=timedelta(seconds=0)),
    },
}


@app.task
def task1():
    print('task1')


@app.task
def task2():
    print('task2')


@app.task
def task3():
    print('task3')

您可以自己编写MySchedule并从BaseSchedule扩展它以获得更多控制权。

答案 2 :(得分:-1)

我试图用一种与vasi1y解决方案略有不同的解决方案来解决这个问题。但是这个解决方案和之前的解决方案都不起作用......

class schedule_offset(schedule):

    def __init__(self, run_every=None, offset=None,
                relative=False, nowfun=None, app=None):
        self._run_every = run_every
        if offset is None:
            offset = 0
        self._offset = maybe_timedelta(offset)
        self._executing = 0
        super(schedule_offset, self).__init__(
            run_every=self._run_every, relative=relative, nowfun=nowfun, app=app)

    def is_due(self, last_run_at):
        last_run_at = last_run_at + self._offset
        last_run_at = self.maybe_make_aware(last_run_at)
        rem_delta = self.remaining_estimate(last_run_at)
        remaining_s = timedelta_seconds(rem_delta)
        if remaining_s == 0:
            ret = schedstate(is_due=True, next=self.seconds + self._offset.seconds)
            if self._executing < 2:
                self._executing += 1
                if self._executing == 2:
                    self._offset = maybe_timedelta(0)
            return ret
        return schedstate(is_due=False, next=remaining_s)

    def __reduce__(self):
        return self.__class__, (self._run_every, self._offset, self.relative, self.nowfun)


CELERYBEAT_SCHEDULE = {
    'task1': {
        'task': 'api.tasks.task1',
        'schedule': schedule_offset(timedelta(seconds=10), offset=2),
    },
    'task2': {
        'task': 'api.tasks.task2',
        'schedule': schedule_offset(timedelta(seconds=30), offset=7),
    },
    'task3': {
        'task': 'api.tasks.task3',
        'schedule': timedelta(seconds=15),
    },
    ...
}