目录[-]

导读: 我们在工作环境中,一定会遇到各种各样的定时任务,需要定时去执行,Linux 有crontab 来进行定时任务,但是python作为脚本语言,在服务器的定时维护有着很大的优越性,就像是天生就是干这的,所以我们就需要找一个能够定时的去执行任务的第三方包,并且还是能够在后台进行执行的第三方库。我们不去比较各种库之间的差异,常见的有celry,apscheduler。工作中用的比较多,django 小脚本,等等都会用到各种各样的定时任务,首先我选择的是apscheduler ,因为他支持跨平台,同时它有着很好的生态,支持django ,flask ,无框架的各种环境,本文主要是针对发生的问题进行讨论。

github 地址:

https://github.com/agronholm/apscheduler

apscheduler 在django 中的使用

apscheduler 在django 中使用的是最多的,同时发生的问题也是最多的,就这些问题,一一展开讨论。 django_apscheduler 是一个一款结合apscheduler 的定时任务的第三方库,网上大多是教大家如何使用,却没有讲到会有那些坑。

问题1:使用django_apscheduler 的过程中,如果你忽略了迁移的任务,直接开始了编写定时任务,然后你想和其他module 文件一起创建迁移文件,并进行迁移的时候,你会发现会一直报错,提示你文件不存在。

答:我们都知道使用它的流程是先在app 中进行注册,然后进行使用,它的迁移文件是提前生成好的,倘若你不先执行迁移,那么你再次进行生成迁移文件的时候就会报错,文件不存在,不过也不是所有的情况会触发,当你将job 任务的view 视图放在url 中生效以后,就会出现这个坑爹的问题。所以如果想解决这个问题,有两个办法:1。先执行迁移命令,然后再进行生成其他module 的迁移文件,再次执行迁移就会解决这个问题。2.先不要再url 文件中引入view 视图中的方法,先进行生成执行文件,然后进行数据迁移,这样也会解决这个问题。

问题2:定时任务不执行?

答:有时候我们遇到定时任务不执行的情况,网上的教程,好多事copy 复制的,所以找半天你也不会找到解决方案,真正的解决方案是,再url 中引入view 视图文件,这样才会真正的执行。

问题3:开发环境中任务被多次执行?

答:出现这种情况的原因是,你开启了debug模式,导致会被重复执行,你会发现每次启动项目,定时任务就会运行一次,就是这个原因。


问题4:生产环境中会出现被多次运行的情况?

答:我们使用uwsgi 进行项目部署,肯定会使用多线程或者是多进程,不然真的扛不住,除了上边说的关闭debug 的情况之外,还会因为uwsgi 是多进程/多线程造成的多次运行,所以我们把进程或者线程调成1 就可以了吗?肯定是不可以的,所以我们就需要用到任务id 的参数。网上讲的例子都很复杂且无效,比如:
https://www.cnblogs.com/shenckicc/p/7019639.html?utm_source=itdadao&utm_medium=referral 
再比如:https://stackoverflow.com/questions/16053364/make-sure-only-one-worker-launches-the-apscheduler-event-in-a-pyramid-web-app-ru/40162246#40162246
麻烦且无效,只需要添加好job_id 即可,看下边的源代码就可以
def add_job(self, func, trigger=None, args=None, kwargs=None, id=None, name=None,misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined,next_run_time=undefined, jobstore='default', executor='default',replace_existing=False, **trigger_args):
        """
        add_job(func, trigger=None, args=None, kwargs=None, id=None, \
            name=None, misfire_grace_time=undefined, coalesce=undefined, \
            max_instances=undefined, next_run_time=undefined, \
            jobstore='default', executor='default', \
            replace_existing=False, **trigger_args)

        Adds the given job to the job list and wakes up the scheduler if it's already running.

        Any option that defaults to ``undefined`` will be replaced with the corresponding default
        value when the job is scheduled (which happens when the scheduler is started, or
        immediately if the scheduler is already running).

        The ``func`` argument can be given either as a callable object or a textual reference in
        the ``package.module:some.object`` format, where the first half (separated by ``:``) is an
        importable module and the second half is a reference to the callable object, relative to
        the module.

        The ``trigger`` argument can either be:
          #. the alias name of the trigger (e.g. ``date``, ``interval`` or ``cron``), in which case
            any extra keyword arguments to this method are passed on to the trigger's constructor
          #. an instance of a trigger class

        :param func: callable (or a textual reference to one) to run at the given time
        :param str|apscheduler.triggers.base.BaseTrigger trigger: trigger that determines when
            ``func`` is called
        :param list|tuple args: list of positional arguments to call func with
        :param dict kwargs: dict of keyword arguments to call func with
        :param str|unicode id: explicit identifier for the job (for modifying it later)
        :param str|unicode name: textual description of the job
        :param int misfire_grace_time: seconds after the designated runtime that the job is still
            allowed to be run
        :param bool coalesce: run once instead of many times if the scheduler determines that the
            job should be run more than once in succession
        :param int max_instances: maximum number of concurrently running instances allowed for this
            job
        :param datetime next_run_time: when to first run the job, regardless of the trigger (pass
            ``None`` to add the job as paused)
        :param str|unicode jobstore: alias of the job store to store the job in
        :param str|unicode executor: alias of the executor to run the job with
        :param bool replace_existing: ``True`` to replace an existing job with the same ``id``
            (but retain the number of runs from the existing one)
        :rtype: Job

        """