【Django功能开发】如何正确使用定时任务(启动、停止)

news2024/11/13 14:53:33

系列文章目录

【Django开发入门】ORM的增删改查和批量操作
【Django功能开发】编写自定义manage命令


文章目录

  • 系列文章目录
  • 前言
  • 一、django定时任务
  • 二、django-apscheduler基本使用
    • 1.安装django-apscheduler
    • 2.配置settings.py的INSTALLED_APPS
    • 3.通过命令生成定时记录表
    • 3.如何创建自定义manage命令
    • 4.创建runapscheduler.py文件
    • 5.修改任务,上一个定时将被清空
    • 6.系统中启动和停止定时
    • 7.如何记录进程id
    • 8.对自定义manage命令添加参数,控制定时任务的开启和停止
  • 二、django-apscheduler参数应用
    • 1.jobstore作业存储
    • 2.scheduler调度器
    • 3.executor执行器
    • 4.trigger触发器
    • 5.job任务管理
      • 第一种方式:调度器条用add_job方法
      • 第二种方式:装饰器执行
    • 6.监听定时任务
  • 总结


前言

顾名思义,定时任务是会在后台一直运行的,但是在使用过程中,我们还是会更新程序的,尤其是在刚开始写定时任务时,会频繁的启动定时程序,但是这样可能有问题的,每次启动程序都是打开了新的定时进程,前边的定时程序可能没有关闭,还在默默的帮你完成之前设定的工作(需要看实际情况)。
网络上虽然有很多教程,其实使用方式都有些问题,真的不敢直接用。这里整理了我自己在django框架中使用定时程序的一点心得,如有需要可自取。


提示:以下是本篇文章正文内容,下面案例可供参考

一、django定时任务

在百度上随便一搜 django定时任务,有很多库都实现了定时任务,比如django-crontab、Celery、django-apscheduler(apscheduler)、time自定义等这些python库,如果在windows系统还可以使用系统自带的定时任务,linux系统自带的crontab任务。
上述这些都可以实现定时任务,在开发中也可能都应用过,本篇博客介绍的是在django框架下开发定时功能,我这里推荐使用的是django-apscheduler,适用于中小系统定时任务功能。

定时任务描述
crontab适用于linux系统
django-crontab适用于linux系统,基于crontab
Celery适用于linux系统,任务队列,大型项目
windows定时适用于windows系统
time适用于多种系统
django-apscheduler适用于多种系统,中小型项目

二、django-apscheduler基本使用

1.安装django-apscheduler

代码如下(示例):

pip install django-apscheduler

2.配置settings.py的INSTALLED_APPS

代码如下(示例):

INSTALLED_APPS = (
    # ...
    "django_apscheduler",
)

3.通过命令生成定时记录表

使用python的manage命令,我们应该cd到项目目录下,就是manage.py文件所在的目录。
在这里插入图片描述

我们应该使用python manage.py makemigrations 和 python manage.py migrate同步数据库,数据库中将生成2个表,django_apscheduler_djangojob和django_apscheduler_djangojobexecution。
在这里插入图片描述

3.如何创建自定义manage命令

自定义命令整理中

4.创建runapscheduler.py文件

runapscheduler.py正是通过自定义创建manange命令的py文件,可以通过python manage.py runapscheduler 启动定时程序。

import logging

from django.conf import settings

from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
from django.core.management.base import BaseCommand
from django_apscheduler.jobstores import DjangoJobStore
from django_apscheduler.models import DjangoJobExecution
from django_apscheduler import util

logger = logging.getLogger(__name__)


def my_job():
  # Your job processing logic here...
  print(123)
  pass

def delete_old_job_executions(max_age=604_800):
  """
  此作业从数据库中删除早于“max_age”的APScheduler作业执行条目。
  它有助于防止数据库中塞满不再有用的旧历史记录。
  最长7天
  """
  DjangoJobExecution.objects.delete_old_job_executions(max_age)


class Command(BaseCommand):
  help = "Runs APScheduler."

  def handle(self, *args, **options):
    scheduler = BlockingScheduler(timezone=settings.TIME_ZONE)
    scheduler.add_jobstore(DjangoJobStore(), "default")

    scheduler.add_job(
      my_job,
      trigger=CronTrigger(second="*/10"),  # Every 10 seconds
      id="my_job",  # The `id` assigned to each job MUST be unique
      max_instances=1,
      replace_existing=True,
    )
    logger.info("Added job 'my_job'.")

    scheduler.add_job(
      delete_old_job_executions,
      trigger=CronTrigger(
        day_of_week="mon", hour="00", minute="00"
      ),  # Midnight on Monday, before start of the next work week.
      id="delete_old_job_executions",
      max_instances=1,
      replace_existing=True,
    )
    logger.info(
      "Added weekly job: 'delete_old_job_executions'."
    )

    try:
      logger.info("Starting scheduler...")
      scheduler.start()
    except KeyboardInterrupt:
      logger.info("Stopping scheduler...")
      scheduler.shutdown()
      logger.info("Scheduler shut down successfully!")

1234这4步是django-apscheduler官网的使用步骤,经过测试,定时程序已经正常运行了。
在这里插入图片描述
现在,数据库中已经有了相关定时任务的记录。
在这里插入图片描述
在这里插入图片描述

5.修改任务,上一个定时将被清空

将步骤4中的runapscheduler文件的my_job改成my_job2,再次python manage.py runapscheduler。
修改的位置
在这里插入图片描述
在这里插入图片描述

新的定时任务和数据库记录如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.系统中启动和停止定时

在linux系统中,我们希望定时任务后台运行,需要执行python manage.py runapscheduler &。
其实,通过python manage.py命令启动的程序,每次都是开启了一个新的进程,进程中开启一个线程去处理定时任务。这样启动的定时任务跟django的web服务是分开,相互之间影像很低。很多博客将定时卸载views.py文件开始位置,定时程序和web服务是在一个进程上,会出现阻塞的情况。
因为某些原因,我们想关闭定时程序,在命令行输入ps -aux | grep python可以查看通过python运行的程序,并且可以查看是哪一个py文件,我的自定义命令文件是start_crontab.py,这里就找start_crontab.py,在通过kill 命令停止该进程。

ps -aux | grep python
# 下图的红框就是我们的启动命令,蓝框是进程id列,725066就是我们定时任务进程id
kill 725066

在这里插入图片描述

7.如何记录进程id

其实无论实在linux还是在windows,我们是可以记录python manage.py runapscheduler任务的进程id的,下边代码会生成一个crontab_pid.txt文件,记录进程id,方便下一次处理和操作。

import os

from apscheduler.schedulers.blocking import BlockingScheduler
from django.core.management import BaseCommand

from feishuapi.tasks import mission_6min
from tianyiapi.settings import BASE_DIR


class Command(BaseCommand):
    # 帮助文本, 一般备注命令的用途及如何使用。
    help = "更新已有任务的状态"

    # 处理命令行参数,可选
    def add_arguments(self, parser):
        pass

    def kill(self, pid):
        # 本函数用于中止传入pid所对应的进程
        if os.name == 'nt':
            # Windows系统
            cmd = 'taskkill /pid ' + str(pid) + ' /f'
            try:
                os.system(cmd)
                print(pid, 'killed')
            except Exception as e:
                print(e)
        elif os.name == 'posix':
            # Linux系统
            cmd = 'kill ' + str(pid)
            try:
                os.system(cmd)
                print(pid, 'killed')
            except Exception as e:
                print(e)
        else:
            print('Undefined os.name')

    # 核心业务逻辑
    def handle(self, *args, **options):
        # 1.读取上次保存的pid
        file = os.path.join(BASE_DIR, 'crontab_pid.txt')
        if os.path.isfile(file):
            with open(file, 'r') as f:
                pid = f.read()
                # 2.如果存在杀死上一次的进程
                # print('上一次进程', pid)
                if pid:
                    # 调用kill函数,终止进程
                    self.kill(pid=pid)
        with open(file, 'w+') as f:
            # 3.获取当前进程的pid
            pid = os.getpid()
            # print('当前进程的pid: ', pid)
            f.write(pid.__str__())

        try:
            # 创建调度器BlockingScheduler()
            scheduler = BlockingScheduler()
            # 添加任务,时间间隔为6分钟
            scheduler.add_job(mission_6min, 'interval', minutes=6, id='test_job1')
            scheduler.start()
        except Exception as e:
            print(e)
        print('定时任务已启动')

8.对自定义manage命令添加参数,控制定时任务的开启和停止

这个目前还没做,有时间再补上。

二、django-apscheduler参数应用

django-apscheduler和apscheduler一样,我们使用的过程中需要根据不同的场景调整定时任务。

1.jobstore作业存储

        scheduler = BlockingScheduler(timezone=settings.TIME_ZONE)
        scheduler.add_jobstore(DjangoJobStore(), "default")

2.scheduler调度器

BlockingScheduler : 当调度器是你应用中唯一要运行的东西时

BackgroundScheduler : 当你没有运行任何其他框架并希望调度器在你应用的后台执行时使用。

AsyncIOScheduler : 当你的程序使用了asyncio(一个异步框架)的时候使用。

GeventScheduler : 当你的程序使用了gevent(高性能的Python并发框架)的时候使用。

TornadoScheduler : 当你的程序基于Tornado(一个web框架)的时候使用。

TwistedScheduler : 当你的程序使用了Twisted(一个异步框架)的时候使用

QtScheduler : 如果你的应用是一个Qt应用的时候可以使用。

from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()
scheduler.start()  # 此处程序不会发生阻塞

scheduler = BlockingScheduler()
scheduler.start()  # 此处程序会发生阻塞

3.executor执行器

通常都是使用默认值,特殊需求可以考虑。

# 方式1: 线程
from apscheduler.executors.pool import ThreadPoolExecutor
executors = {
    'default': ThreadPoolExecutor(20)   # 最多20个线程同时执行
}
scheduler = BackgroundScheduler(executors=executors)


# 方式2: 进程
from apscheduler.executors.pool import ProcessPoolExecutor
executors = {
    'default': ProcessPoolExecutor(3)  # 最多3个进程同时运行
}
scheduler = BackgroundScheduler(executors=executors)

4.trigger触发器

APScheduler 有三种内建的 trigger:

date: 特定的时间点触发
interval: 固定时间间隔触发
cron: 在特定时间周期性地触发

from datetime import date

# 在2020年11月11日00:00:00执行
sched.add_job(my_job, 'date', run_date=date(2020, 11, 11))

# 在2020年11月1日16:30:05
sched.add_job(my_job, 'date', run_date=datetime(2020, 11, 11, 16, 30, 5))
sched.add_job(my_job, 'date', run_date='2009-11-06 16:30:05')

# 立即执行
sched.add_job(my_job, 'date')  
sched.start()
from datetime import datetime

# 时间间隔可选seconds 、minutes、hours、days、weeks、start_date、end_date 、timezone
# 每两小时执行一次
sched.add_job(job_function, 'interval', hours=2)

# 在2010年10月10日09:30:00 到2014年6月15日的时间内,每两小时执行一次
sched.add_job(job_function, 'interval', hours=2, start_date='2010-10-10 09:30:00', end_date='2014-06-15 11:00:00')
# 可选周期second、minute、hour、day、day_of_week、month、year、start_date、end_date、timezone
# 在6、7、8、11、12月的第三个周五的00:00, 01:00, 02:00和03:00 执行
sched.add_job(job_function, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')

# 在2014年5月30日前的周一到周五的5:30执行
sched.add_job(job_function, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2014-05-30')

5.job任务管理

通常添加job有2中方式。

第一种方式:调度器条用add_job方法

def my_job():
    # Your job processing logic here...
    print(123)
    pass
job = scheduler.add_job(my_job, 'interval', minutes=2)  # 添加任务

第二种方式:装饰器执行

scheduler = BlockingScheduler(timezone=settings.TIME_ZONE)
scheduler.add_jobstore(DjangoJobStore(), "default")
@register_job(scheduler, "interval", seconds=1)
def dummy_job():
    print("I'm a job!")
scheduler.start()

涉及到具体定时任务,可以通过id参数进行管理。

# 方式1: 通过对象
job = scheduler.add_job(myfunc, 'interval', minutes=2)  # 添加任务
job.remove()  # 移除任务
job.pause() # 暂停任务
job.resume()  # 恢复任务

# 方式2: 通过任务id
scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id')  # 添加任务    
scheduler.remove_job('my_job_id')  # 移除任务
scheduler.pause_job('my_job_id')  # 暂停任务
scheduler.resume_job('my_job_id')  # 恢复任务

scheduler.shutdown()  # 停止任务

6.监听定时任务

# coding:utf-8

from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR, EVENT_JOB_MISSED
import datetime
import logging

logger = logging.getLogger('job')
logging.basicConfig(level=logging.INFO,
                    format = '%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt = '%Y-%m-%d %H:%M:%S',
                    filename = 'mylog.txt',
                    filemode = 'a')

'''任务1'''
def my_job(x):
    print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), x)

'''任务2'''
def test_job(x):
    print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), x)
    print(1 / 0)

def job_listener(Event):
    job = scheduler.get_job(Event.job_id)
    if not Event.exception:
        print('任务正常运行!')
        logger.info("jobname=%s|jobtrigger=%s|jobtime=%s|retval=%s", job.name, job.trigger,
                    Event.scheduled_run_time, Event.retval)

    else:
        print("任务出错了!!!!!")
        logger.error("jobname=%s|jobtrigger=%s|errcode=%s|exception=[%s]|traceback=[%s]|scheduled_time=%s", job.name,
                     job.trigger, Event.code,
                     Event.exception, Event.traceback, Event.scheduled_run_time)

scheduler = BlockingScheduler()
scheduler.add_job(func=test_job, args=('一次性任务,会出错',),
                 next_run_time=datetime.datetime.now() + datetime.timedelta(seconds=15), id='date_task')
scheduler.add_job(func=my_job, args=('循环任务',), trigger='interval', seconds=3, id='interval_task')
scheduler.add_listener(job_listener, EVENT_JOB_ERROR | EVENT_JOB_MISSED | EVENT_JOB_EXECUTED)
scheduler._logger = logging
scheduler.start()

总结

以上就是我对定时任务的理解,在接下来的工作中可以更好的应用定时任务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/377866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL索引类型(type)分析

type索引类型 system > const > eq_ref > ref > range > index > all 优化级别从左往右递减,没有索引的⼀般为’all’。推荐优化目标:至少要达到 range 级别, 要求是 ref 级别, 如果可以是 const 最好&#xff…

线程池源码和CompletableFuture使用总结

线程池 线程池的创建方式 通过Executors的静态方法通过 new ThreadPoolExecutor方式创建 七大参数的作用 参数作用corePoolSize核心线程数,线程池创建好后就准备就绪的线程数量,一直存在maximumPoolSize最大线程数量,控制资源keepAliveTim…

嵌入式 linux 系统开发网络的设置

目录 一、前言 二、linux网络静态地址设置 前言 为什么要对linux系统下的ubuntu进行网络设置呢? 因为我们在嵌入式开发中,我们要保证windows系统、linux系统、开发板的ip要处于同一个网段,而默认ubuntu下的linux系统的ip是动态分配的&#…

ACM-蓝桥杯训练第一周

🚀write in front🚀 📝个人主页:认真写博客的夏目浅石.CSDN 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝​ 📣系列专栏:ACM周训练题目合集.CSDN 💬总结&#xff1a…

debian 部署nginx https

我是flask 处理请求单进程, 差点意思 , 考虑先flask 在往下走 一:安装nginx 因为我是debian 系统,所以我的建议是直接 sudo apt-get install nginx 你也可以选择在官网下载, 但是我搭建ssl 的时候安装openssl非常的麻…

记住这3点,有效提高江苏专转本上岸率

记住这3点,有效提高上岸率 我们都知道,在江苏统招专转本考试中想岸并不是一件容易的事情。考生能否顺利上岸,往往受多方面因素影响,这其中包括:个人基础、学习方式、信息搜索能力。 如何提高自己的专转本上岸几率&…

粗心小编被云拯救,那云上数据谁来拯救?

开工第三天      小编已忙的焦头烂额      不是因为工作积压      而是因为自己的疏忽      也许是没有进入工作状态,一大早先经历了自行车钥匙丢失,手机遗落在家,好不容易坐到工位上才发现,备份数据的U盘忘带了。    不过,好在提前将工作文件上传到了云端…

10 大项目管理知识领域 4大核心领域 5大辅助领域

有人说:一个人从1岁活到80岁很平凡,但如果从80岁倒着活,那么一半以上的人都可能不凡。 生活没有捷径,我们踩过的坑都成为了生活的经验,这些经验越早知道,你要走的弯路就会越少。 项目管理的10大知识领域&a…

LeetCode 1145. 二叉树着色游戏 -- 简单搜索

二叉树着色游戏 提示 中等 199 相关企业 有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中,给出二叉树的根节点 root,树上总共有 n 个节点,且 n 为奇数,其中每个节点上的值从 1 到 n 各不相同。 最开始时: 「一…

【C++】vector的基本使用

难道向上攀爬的那条路,不是比站在顶峰更让人热血沸腾吗? 文章目录一、vector和string的联系与不同二、vector的扩容操作1.resize() (缺省值为匿名对象)&& reserve()2.reserve在g和vs上的扩容机制3.reserve异地扩容和shri…

Pinia 介绍、使用、实践

1. Pinia 介绍1.1 Pinia 是什么Pinia 官网https://pinia.vuejs.org/vuex Githubhttps://github.com/vuejs/vuex上面是 Vuex Github 中置顶说明,我们可以得知:Pinia 现在是新的默认设置,Vue 的官方状态管理库已更改为 Pinia,Vue3、…

数据结构与算法系列之时间与空间复杂度

这里写目录标题算法的复杂度大O的渐进表示法实例分析空间复杂度每日一题算法的复杂度 衡量一个算法的好坏,一般 是从时间和空间两个维度来衡量的, 即时间复杂度和空间复杂度。 时间复杂度主要衡量一个算法的运行快慢, 空间复杂度主要衡量一个…

Linux -- 程序 进程 线程 概念引入

程序与进程 :程序 :什么是程序 ???伪官方 : 二进制文件,文件存储在磁盘中,例如 /usr/bin 目录下 。 是静态。 简单讲 :# 我们都学习了语言,比如下面这串代…

全国领先——液力悬浮仿生型人工心脏上市后在同济医院成功植入

2023年2月22日,华中科技大学同济医学院附属同济医院(同济医院)心脏大血管外科团队举办了一场气氛热烈的小规模庆祝活动,魏翔主任、程才副主任、王星宇副主任医师和李师亮医师到场,为终末期心衰患者黄先生“庆生”&…

Java 文本检索神器 “正则表达式”

Java 文本检索神器 “正则表达式” 每博一文案 在我们短促而又漫长的一生中,我们在苦苦地寻找人生的幸福,可幸福往往又与我们失之交臂, 当我们为此而耗尽宝贵的。青春年华,皱纹也悄悄地爬上了眼角的时候,我们或许才能…

Maven工程打jar包的N种方式

Maven工程打jar包 一、IDEA自带打包插件二、maven插件打包2.1 制作瘦包(直接打包,不打包依赖包)2.2 制作瘦包和依赖包(相互分离)2.3 制作胖包(项目依赖包和项目打为一个包)2.4 制作胖包&#xf…

数据结构与算法(二十)快速排序、堆排序(四)

数据结构与算法(三)软件设计(十九)https://blog.csdn.net/ke1ying/article/details/129252205 排序 分为 稳定排序 和 不稳定排序 内排序 和 外排序 内排序指在内存里,外排序指在外部存储空间排序 1、排序的方法分类。 插入排序&#xff…

下拉框推荐-Suggest-SUG

什么是下拉框推荐 在我们使用各种app(飞猪)想要搜索我们想要的东西,假设我想要上海迪士尼的门票,那么精确的query是“上海迪士尼门票”,要打7个字,如果在你输入“上海”的时候app就推荐了query“上海迪士尼…

无线蓝牙耳机哪个牌子好?2023质量好的无线蓝牙耳机推荐

近几年,随着蓝牙技术的不断进步,使用蓝牙耳机的人也越来越多。蓝牙耳机的出现,不仅能让我们摆脱线带来的约束,还能提升我们学习和工作的效率。最近看到很多人问,无线蓝牙耳机哪个牌子好?下面,我…

accent-color一行代码,让你的表单组件变好看

不做切图仔,从关注本专栏开始 文章目录 不做切图仔,从关注本专栏开始前言兼容性语法继承性智能前言 在之前的网站开发中,我们是很难去更改的你某些控件的颜色。我们可能要使用各种技巧来自定义我们的控件。好消息是,今天如果我们想要去改变控件的颜色,css为我们提供了一些…