最近需要考虑如何在django环境中跑定时任务. 这个在  stackoverflow 也有对应的 讨论 , 方法也有不少, 这边简单尝试和总结下.

假设我们现在的定期任务就是睡眠  n 秒, 然后往数据库里面写一条记录, 记录这个任务的起始以及结束时间, 并且我们  不关心 该任务的返回结果. 项目名称为  mmtest , 应用名称为  mma_cron (说实话我也不知道自己怎么取这样的名称….), 数据库使用 sqlite , model代码如下:

mma_cron/models.py

from django.db import models
from django.utils import timezone # Create your models here. class SimpleTask(models.Model):
task_name = models.CharField(max_length=200)
start_time = models.DateTimeField('task begin time', default=timezone.now)
finish_time = models.DateTimeField('task end time', blank=True, null=True)

最朴素的方法

考虑一个最最朴素的实现: 起一个简单的daemon程序, 每隔一定时间进行任务处理; 或者是写一个简单的程序, 利用  cron 来定期执行该脚本. 一个好处是足够简单. 但很多时候写这样的程序需要花不少时间.

简单的改进

自己重新写一个程序有时候代价还是挺大的, 尤其是业务逻辑十分复杂的时候, 所以可以考虑复用已有的代码. 如果考虑  cron 定期执行的策略, 我们可以在  django 中实现对应逻辑, 方法也有几个:

  1. 实现一个api, 定期请求
  2. 扩展manage.py

方法一没什么好说的, 但是需要考虑访问控制; 方法二的话也很简单, 可以参考  自定义commands . 无论用哪种方式, 我们可以先实现对应的任务处理逻辑:

mma_cron/models.py

def run_simple_task(task_name):
task = SimpleTask(task_name = task_name)
task.save() ## do a lot lot of stuff......
seconds = random.randint(5, 15)
time.sleep(seconds) task.finish_time = timezone.now()
task.save() return seconds

针对方法一, 实现对应的view:

mma_cron/views.py

from django.http import HttpResponse

from .models import run_simple_task

def do_task(request, task_name):
seconds = run_simple_task(task_name)
return HttpResponse("I have done task in %d sec: %s" % (seconds, task_name))

添加对应的route:

mma_cron/urls.py

urlpatterns = [
## add one
url(r'^task/(?P<task_name>\w+)', views.do_task, name='do_task'),
]

我们手动访问试一下:  curl 127.0.0.1:8000/mma_cron/task/ddddd , 可以看到在经过一段时间后数据库中有结果了. 可行~

针对方法二, 扩展  manage.py , 实现对应的command:

mma_cron/management/commands/yy.py

from django.core.management.base import BaseCommand, CommandError
from mma_cron.models import run_simple_task class Command(BaseCommand):
help = 'Run the simple command' def add_arguments(self, parser):
pass def handle(self, *args, **options):
seconds = run_simple_task('run from manage.py')
self.stdout.write("task done")

再手动试一下:  python manage.py yy , 发现也有对应的结果, 可行~

至于定期可以通过设定cron来实现.

插件

上面的实现比较简便, 也很有效. django的插件也有对应的实现, 这里着重介绍一下 django-cron . 它类似使用了上面提到的方法二, 在此之上增加了  任务执行检查 ,  日志记录 等功能. 我们也来看一下;)

安装的话推荐直接把源码下的  django_cron (相当于一个django的应用)目录放到当前工程目录. 使用可以参考  这里 . 接下来需要执行下它的迁移操作(migration):

python manage.py migrate django_cron

该操作是创建对应的数据库, 之后执行定时任务时会把  时间 和  相关结果 保存到数据库中. 接下来我们需要在  mma_cron 中定义一个对应的定时任务:

mma_cron/cron.rb

from django.utils import timezone
from django_cron import CronJobBase, Schedule
from .models import run_simple_task class SimpleTaskCronJob(CronJobBase):
RUN_EVERY_MINS = 1 schedule = Schedule(run_every_mins=RUN_EVERY_MINS)
code = 'mma_cron.cron.simple_task_cron_job' def do(self):
seconds = run_simple_task('task from django-cron') msg = "I have done task in %s sec: %s" return ("I have done task in %s sec: %s" % (seconds, task_name))

可以看到里面定义了该定时任务的  处理间隔 与相应动作. 最后我们还需要在 settings.py 注册一下我们的定时任务:

mmtest/settings.py

CRON_CLASSES = [
"mma_cron.cron.SimpleTaskCronJob",
]

大功告成, 接下来我们只需在执行  python manage.py runcrons 即可跑我们的任务了. 运行时  django_cron 会根据任务的执行时间与数据库中记录的时间做对比, 如果没到对应时间会直接退出. 每次执行完都会在  django_cron_cronjoblog 这张表中记录对应的时间以及返回的结果(原来python不是默认把最后一个语句的结果当成返回值的, 好吧). 同样的, 为了让其定时跑, 我们也需要在crontab中添加对应的命令.

值得一提的是如果注册了多个任务, 这些任务默认是串行执行的(考虑到安全性). 如果想要并行执行需要改一些设置, 详见文档.

Celery

上述几种方法都比较简单, 但是我们还有其他的方式;) 比如很多人都提到的  Celery. 给我的感觉它很像  gearman 和  sidekiq , 类似一个任务分发和处理框架. 利用它的 Periodic Tasks , 可以实现定期发布任务让worker取执行任务. 我们也来简单看看.

简单尝试

我们先不管django, 先感性的了解下Celery. Celery的  任务分发 和  结果存储 需要依赖外部组件, 可选的有  RabbitMQ ,  Redis , 数据库等等. 简单起见, 这里选择使用  redis(  RabbitMQ 部署起来还是稍微复杂了点).

首先根据官方的  教程 , 创建个分发  加法 和  乘法 任务的系统. 先来看看  worker 的代码:

proj/tasks.py

from __future__ import absolute_import

from proj.celery import app

@app.task
def add(x, y):
return x + y @app.task
def mul(x, y):
return x * y @app.task
def xsum(numbers):
return sum(numbers)

再来看看  celery 的设置代码:

proj/celery.py

from __future__ import absolute_import

from celery import Celery
from datetime import timedelta app = Celery('proj',
broker='redis://localhost',
backend='redis://localhost',
include=['proj.tasks']) ## 这段代码可以先忽略;)
app.conf.update(
CELERY_TASK_RESULT_EXPIRES = 3600,
CELERYBEAT_SCHEDULE = {
'add-every-30-seconds': {
'task': 'proj.tasks.add',
'schedule': timedelta(seconds=30),
'args': (16, 32),
},
},
) if __name__ == '__main__':
app.start()

我们先启动  redis , 然后通过执行  celery -A proj worker -l info 来启动一个  worker . 接下来我们手动来创建任务, 然后丢给worker:

python

>>> import proj.tasks
>>> result = proj.tasks.add.delay(2,2)
>>> result.get()
4

这样就创建一个任务, 我们在celery的控制台(worker进程)可以看到这样的输出:

celery

Received task: proj.tasks.add[a24a9792-a7c6-4f64-994d-ca6903b3182c]
Task proj.tasks.add[a24a9792-a7c6-4f64-994d-ca6903b3182c] succeeded in 0.0018904690005s: 4

很直观~ 我们同时也可以看看它在redis里面是什么个样子:

redis-cli

127.0.0.1:6379> keys *
1) "celery-task-meta-a24a9792-a7c6-4f64-994d-ca6903b3182c"
2) "_kombu.binding.celery.pidbox"
3) "_kombu.binding.celery"
4) "_kombu.binding.celeryev" 127.0.0.1:6379> TYPE _kombu.binding.celery
set

现在我们是手动来创建任务, 我们可以启动一个定时生产任务的生产者  celery -A proj beat -l info . 它会定期创建我们在  proj/celery.py 代码中指定的 CELERYBEAT_SCHEDULE 中的任务.

结合django

理解了celery, 再结合django就相对比较简单了. 推荐阅读一下官方的  文档 . 结合我们的SimpleTask的例子, 先设定下celery的任务:

mmtest/celery.py

from __future__ import absolute_import

import os

from celery import Celery
from datetime import timedelta os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mmtest.settings') from django.conf import settings app = Celery('mmtest', broker='redis://localhost') app.config_from_object('django.conf:settings') ## 自动发现任务: APP/tasks.py
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) ## 设置定时任务参数
app.conf.update(
CELERYBEAT_SCHEDULE = {
'do-task-every-30-seconds': {
'task': 'mma_cron.tasks.do_task',
'schedule': timedelta(seconds=30),
},
},
) @app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))

接下来我们需要实现一下我们的worker:

mma_cron/tasks.py

from __future__ import absolute_import

from celery import shared_task
from .cron import run_simple_task @shared_task
def do_task():
run_simple_task('run by celery')

不需要其他的工作, 我们就完成了我们的目的. 接下来就是分别启动  worker 和  beat了. 这也实现了我们的定时任务的目的(就我们这个例子有点大材小用了).

django的crontab的更多相关文章

  1. django crontab 定时任务

    分 时 日 月 周 命令(最好用绝对路径)比如: * * * * * rm -fr /mnt/* //每分钟执行一次对/mnt目录下文件的删除*/2 * * * * rm -fr /mnt/* //每 ...

  2. 记录一次 Linux crontab 执行django 脚本 失败 的经历和解决办法

    目的是想通过定时任务来执行一次数据统计,本来可以用celery来做,但是想着这个项目整个就没用到异步的地方,所以决定用crontab来做.之前做过数据库的热备份,想来用该没啥问题,但是现实打脸啪啪响. ...

  3. 【Django】Django 定时任务实现(django-crontab+command)

    一.编写自定义django-admin命令 注:利用django-admin自定义命令我们可以ORM框架对model进行操作,如:定时更新数据库,检测数据库状态..... Django为项目中每一个应 ...

  4. 如何利用Cron让django应用定期执行

    最近用Django写了一个项目,但是有一个地方需要应用在后台自动定期执行检查,并存入数据库,如果单纯的写Python程序的话不能很好的跟django的结合在一起,写起来也和麻烦,查找资料的时候发现了d ...

  5. apache(nginx)+django+virutalenv(virtualenvwrapper)+gunicorn+supervisor配置高效web环境

    前言 django的调试模式配置简单,用于测试十分方便,但众所周知,这个只适合于调试,生产上运行效率十分低下. 后来考虑用nginx+uwsgi的模式进行,但之前配置过apache+wsgi的方式,感 ...

  6. 如何让django方法自动地定期执行

    实现思路:1.首先把需要自动执行的django method写成django command2.将自己定义的django command添加到cron中使用cron服务实现定期执行 Part1 在dj ...

  7. [转]crontab环境变量设置

    原文连接:http://blog.csdn.net/zc02051126/article/details/20480289 come from http://www.360doc.com/conten ...

  8. Django中如何使用django-celery完成异步任务1(转)

    原文链接: http://www.weiguda.com/blog/73/ 本篇博文主要介绍在开发环境中的celery使用,请勿用于部署服务器. 许多Django应用需要执行异步任务, 以便不耽误ht ...

  9. django定期执行任务

    要在django项目中定期执行任务,比如每天一定的时间点抓取数据,刷新数据库等,可以参考stackoverflow的方法,先编写一个manage.py命令,然后使用crontab来定时执行这个命令. ...

随机推荐

  1. R语言低级绘图函数-arrows

    arrows 函数用来在一张图表上添加箭头,只需要分别指定起始坐标和终止坐标,就可以添加箭头了,还可以通过一些属性对箭头的形状,大小进行调整 基本用法: xo, yo 指定起始点的x和y坐标,x1, ...

  2. markdown 转 pdf 方法

    (1)Mou: (macosx 系统下的markdown编辑器,转pdf完美,推荐) http://25.io/mou/ (2)Chrome 打印 (打印得很好看,缺点是转好的pdf上的文字有时候不能 ...

  3. oracle中如何把结果集插入临时表中

    比如临时表叫temp,你要查询的语句为select * from 表名 where id=1. 1.建表temp 2.插入语句: ; commit;  

  4. JUC回顾之-线程池的原理和使用

    Java并发编程:线程池的使用   Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程 ...

  5. OpenCV学习:Mat结构中的数据共享机制

    使用Mat类,内存管理变得简单,不再像使用IplImage那样需要自己申请和释放内存. Mat是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩 ...

  6. 移动HTML 5前端性能优化指南

    概述 PC优化手段在Mobile侧同样适用 在Mobile侧我们提出三秒种渲染完成首屏指标 基于第二点,首屏加载3秒完成或使用Loading 基于联通3G网络平均338KB/s(2.71Mb/s),所 ...

  7. HDU - 2089 不要62 (暴力或数位DP)

    Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局常常会扩充一些的士车牌照.新近出来一个好消息.以后上牌照,不再含有不吉利的数字了.这样一来.就能够消除个别 ...

  8. UVA 1341 - Different Digits(数论)

    UVA 1341 - Different Digits 题目链接 题意:给定一个正整数n.求一个kn使得kn上用的数字最少.假设同样,则输出值最小的 思路: 首先利用鸽笼原理证明最多须要2个数字去组成 ...

  9. swift学习笔记之--方法

    一.说明 跟oc一样,面向对象,swift重点额方法可以分为2大类: (1)实例方法 oc中为减号方法(对象方法) (2)类型方法 oc中的加号方法(类方法) 二.实例方法 只能是对象调用的方法 代码 ...

  10. Nginx(二)-- 配置文件之虚拟主机配置

    1.配置文件与解释 #user nobody; worker_processes 1; # 设置工作子进程,默认是1个工作子进程,可以修改,一般设置为CPU的总核数 #error_log logs/e ...