py定时库APScheduler初识及 skipped: maximum number of running instances reached 错误排查

需求

手头小程序,需求python写个定时脚本,调用java 程序清理程序一些脏数据。
临时找到这个 APScheduler 定时库,准备用一次

简介

简单介绍如下:

高级Python调度程序(APScheduler)是一个Python库,可让您安排稍后执行的Python代码,可以是一次,也可以是定期执行。 您可以根据需要随时添加新作业或删除旧作业。 如果将作业存储在数据库中,它们也将在调度程序重新启动后继续运行并保持其状态。 重新启动调度程序后,它将运行它应该在脱机时运行的所有作业[1]。

除此之外,APScheduler还可以用作特定于平台的调度程序(如cron守护程序或Windows任务调度程序)的跨平台,特定于应用程序的替代程序。 但请注意,APScheduler本身不是守护程序或服务,也不附带任何命令行工具。 它主要用于在现有应用程序中运行。 也就是说,APScheduler确实为您提供了一些构建块来构建调度程序服务或运行专用的调度程序进程。

APScheduler有三个可以使用的内置调度系统:

  • Cron(可选的开始/结束时间,周期内执行)

  • 间隔性运行(间隔运行作业,可选的开始/结束时间,比如每隔2小时)

  • 一次性延迟执行(在设定的日期/时间运行一次作业)

安装

安装非常简单,会用 pip 的人都知道

pip install apscheduler

基本概念介绍

APScheduler有四种组件

  • 触发器(triggers):触发器包含调度逻辑,描述一个任务何时被触发,按日期或按时间间隔或按 cronjob 表达式三种方式触发。每个作业都有它自己的触发器,除了初始配置之外,触发器是完全无状态的。

  • 作业存储器(job stores):作业存储器指定了作业被存放的位置,默认情况下作业保存在内存,也可将作业保存在各种数据库中,当作业被存放在数据库中时,它会被序列化,当被重新加载时会反序列化。作业存储器充当保存、加载、更新和查找作业的中间商。在调度器之间不能共享作业存储。

  • 执行器(executors):执行器是将指定的作业(调用函数)提交到线程池或进程池中运行,当任务完成时,执行器通知调度器触发相应的事件。

  • 调度器(schedulers):任务调度器,属于控制角色,通过它配置作业存储器、执行器和触发器,添加、修改和删除任务。调度器协调触发器、作业存储器、执行器的运行,通常只有一个调度程序运行在应用程序中,开发人员通常不需要直接处理作业存储器、执行器或触发器,配置作业存储器和执行器是通过调度器来完成的。

##选择正确的调度程序、作业存储、执行程序和触发器

您选择的调度程序主要取决于您的编程环境以及您将使用APScheduler的内容。 以下是选择调度程序的快速指南:

  • BlockingScheduler:当调度程序是您的进程中唯一运行的时候使用
  • BackgroundScheduler:当你没有使用下面的任何框架时使用,并希望调度程序在应用程序内部的后台运行
  • AsyncIOScheduler:如果您的应用程序使用asyncio模块,请使用
  • GeventScheduler:如果您的应用程序使用gevent,请使用
  • TornadoScheduler:如果您正在构建Tornado应用程序,请使用它
  • TwistedScheduler:如果你正在构建一个Twisted应用程序,请使用它
  • QtScheduler:如果你正在构建一个Qt应用程序,请使用它

要选择适当的作业存储,您需要确定是否需要作业持久性。 如果您总是在应用程序启动时重新创建作业,那么您可以使用默认值(MemoryJobStore)。 但是,如果您需要将作业保留在调度程序重新启动或应用程序崩溃之上,那么您的选择通常可以归结为编程环境中使用的工具。 但是,如果您可以自由选择,那么由于其强大的数据完整性保护,PostgreSQL后端上的SQLAlchemyJobStore是推荐的选择。

同样,如果您使用上面的框架之一,通常也会为您选择executor。否则,默认的ThreadPoolExecutor应该足以满足大多数目的。如果工作负载涉及CPU密集型操作,则应该考虑使用ProcessPoolExecutor来使用多个CPU内核。您甚至可以同时使用这两种方法,将流程池执行器添加为辅助执行器。

更多内容请查看

github : https://github.com/agronholm/apscheduler

官网文档:https://apscheduler.readthedocs.io/en/latest/

好吧说了这么多 准备开干了. 上代码

# -*- coding: utf-8 -*-
import threading
from datetime import datetime
from datetime import timedelta
import os
from apscheduler.schedulers.blocking import BlockingScheduler
import calendar

# 执行任务
def tick():
    print('开始执行任务 The time is: %s' % datetime.now())
    print('The time is: %s' % datetime.now()+" 开始时间"+begin_time+"到"+end_time+"之间无用的信息")
    os.system('nohup java -jar 小程序.jar 2&>1 &')
    print('删除多余信息脚本执行完毕 ! The time is: %s' % datetime.now())

if __name__ == '__main__':
    # 配置调度器
    job_defaults = { 'max_instances': 3 }
    scheduler = BlockingScheduler(job_defaults=job_defaults)
    scheduler.add_job(tick,'cron', month='*', day='1', hour='1', minute='30',second='30')
    print("开始执行删除")

    try:
        scheduler.start()
    except (KeyboardInterrupt, SystemExit):
        pass

好,本地测试可以完成。后来使用发现报错,问题如下:

Execution of job "tick (trigger: cron[month='*', day='1', hour='1', minute='30',second='30'], next run at: 2020-01-01 01:30:30 CST)" skipped: maximum number of running instances reached (1)

查询了一下原因说是 任务执行的时间太长。定位问题到 os.system 命令,此命令执行完会有返回值。定时程序执行到这里后就一直等待返回,然后没有返回就卡顿了。嗯。。。。。。

明白了原因想到了一个办法,我加了一个中转的 shell 脚本,通过py 脚本,调用 shell 脚本,shell 脚本调用 Java程序。py 脚本,调用 shell 脚本后就会拿到返回值。定时程序就没问题了。等待下次定时执行。 java 程序就在后台自行的运行。

修改如下:

    os.system('sh /date/DeleteStart.sh')

新增shell 脚本:

#bin/bash
echo "启动定时删除redis 多余key java程序 开始时间'$begin_time' 结束时间 '$end_time'"  
nohup java -jar xxx-1.0.0.jar 
echo "启动成功"  

OK,打完收工。

发布了343 篇原创文章 · 获赞 649 · 访问量 231万+

猜你喜欢

转载自blog.csdn.net/u012373815/article/details/103887928
今日推荐