flask- APScheduler动态加持久化定时任务

概念说明:

APScheduler 支持三种调度任务:固定时间间隔,固定时间点(日期),Linux 下的 Crontab 命令。
APScheduler 使用起来还算是比较简单。运行一个调度任务只需要以下三部曲。

  1. 新建一个 schedulers (调度器) 。
  2. 添加一个调度任务(job stores)。
  3. 运行调度任务

基础组件:

APScheduler 有四种组件,分别是:调度器(scheduler),作业存储(job store),触发器(trigger),执行器(executor)。

schedulers(调度器)

它是任务调度器,属于控制器角色。它配置作业存储器和执行器可以在调度器中完成,例如添加、修改和移除作业。
它提供 7 种调度器,能够满足我们各种场景的需要。例如:后台执行某个操作,异步执行操作等。调度器分别是:

  • BlockingScheduler : 调度器在当前进程的主线程中运行,也就是会阻塞当前线程。
  • BackgroundScheduler : 调度器在后台线程中运行,不会阻塞当前线程。
  • AsyncIOScheduler : 结合 asyncio 模块(一个异步框架)一起使用。
  • GeventScheduler : 程序中使用 gevent(高性能的Python并发框架)作为IO模型,和 GeventExecutor 配合使用。
  • TornadoScheduler : 程序中使用 Tornado(一个web框架)的IO模型,用 ioloop.add_timeout 完成定时唤醒。
  • TwistedScheduler : 配合 TwistedExecutor,用 reactor.callLater 完成定时唤醒。
  • QtScheduler : 你的应用是一个 Qt 应用,需使用QTimer完成定时唤醒。

job stores(作业存储器)

job信息默认是存到内存里面,服务重启后job信息会消失,当使用场景的job是需要反复运行时,可以将job信息持久化。

触发器(trigger)

date是最基本的一种调度,作业任务只会执行一次。
interval触发器,固定时间间隔触发。
cron触发器,在特定时间周期性地触发,和Linux crontab格式兼容。它是功能最强大的触发器

1)date 触发器

date 是最基本的一种调度,作业任务只会执行一次。它表示特定的时间点触发。它的参数如下:
参数 说明
run_date (datetime 或 str) 作业的运行日期或时间
timezone (datetime.tzinfo 或 str) 指定时区
func 要运行的方法
trigger 触发器类型

def my_job():
    print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
# 在 2020-08-13 14:00:01 时刻运行一次 my_job 方法
scheduler .add_job(func="my_job", trigger='date', run_date='2020-08-13 14:00:01',id="1")

2)interval 触发器

固定时间间隔触发。interval 间隔调度,参数如下:
参数 说明
weeks (int) 间隔几周
days (int) 间隔几天
hours (int) 间隔几小时
minutes (int) 间隔几分钟
seconds (int) 间隔多少秒
start_date (datetime 或 str) 开始日期
end_date (datetime 或 str) 结束日期
timezone (datetime.tzinfo 或str) 时区
code:

# 在 2020-12-13 14:00:01 ~ 2020-12-13 15:00:10 之间, 每隔两分钟执行一次 job_func 方法
scheduler .add_job(job_func, 
'interval', minutes=2, 
start_date='2020-12-13 14:00:01' , 
end_date='2020-12-13 15:00:10')

3)cron 触发器

在特定时间周期性地触发,和Linux crontab格式兼容。它是功能最强大的触发器。
我们先了解 cron 参数:
参数 说明
year (int 或 str) 年,4位数字
month (int 或 str) 月 (范围1-12)
day (int 或 str) 日 (范围1-31
week (int 或 str) 周 (范围1-53)
day_of_week (int 或 str) 周内第几天或者星期几 (范围0-6 或者 mon,tue,wed,thu,fri,sat,sun)
hour (int 或 str) 时 (范围0-23)
minute (int 或 str) 分 (范围0-59)
second (int 或 str) 秒 (范围0-59)
start_date (datetime 或 str) 最早开始日期(包含)
end_date (datetime 或 str) 最晚结束时间(包含)
timezone (datetime.tzinfo 或str) 指定时区
code:

# 在每年 1-3、7-9 月份中的每个星期一、二中的 00:00, 01:00, 02:00 和 03:00 执行 job_func 任务
scheduler .add_job(job_func,
 'cron', month='1-3,7-9',day='0, tue', hour='0-3')
# 每周1到3 晚上11:10:00 执行job_func 任务
scheduler .add_job(job_func,
 'cron', day_of_week='1-3',hour='11', minute='10',second='00')
# 每周1和周3 晚上11:10:00 执行job_func 任务
scheduler .add_job(job_func,
 'cron', day_of_week='1,3',hour='11', minute='10',second='00')

持久化

持久化可以用redis,mysql,mongodb等多种数据库,下面用mysql作为示例:
设置持久化数据库地址后,会自动在库中新增apscheduler_jobs 表储存job 信息,里面记录job 的id,下一次运行时间,以及job状态,interval 类型job执行完后会自动从表中删除。
在这里插入图片描述
持久化配置:

# mysql 数据库持久化配置
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://root:123456@127.0.0.1:3306/mocker-api"
    # 存储位置
    SCHEDULER_JOBSTORES = {
        'default': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URI)
    }
# MongoDB 数据库持久化配置
from apscheduler.jobstores.mongodb import MongoDBJobStore
SCHEDULER_JOBSTORES = {
        'default': MongoDBJobStore(host='mongoserver', port=27017)
    }

实战(任务增删改暂停启动查)

项目结构:
|—app
| |—init.py
| | ---- task.py
| | —settings.py
| | —extensions.py
| | —view.py

init.py:

from extensions import scheduler
def create_app(config_name=None):
	app = Flask(__name__)
	app.config.from_object(config[config_name])
	# 启动apscheduler服务
	scheduler.start()

extensions.py

from flask_apscheduler import APScheduler
# 实例APScheduler定时任务
scheduler = APScheduler()

settings.py

class DevelopmentConfig():
	SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://root:123456@127.0.0.1:3306/mocker-api"
	# 调度器开关
	SCHEDULER_API_ENABLED = True
	# -------持久化配置---------
	# job存储位置
	SCHEDULER_JOBSTORES = {
	    'default': SQLAlchemyJobStore(url=SQLALCHEMY_DATABASE_URI)
	}
	# 线程池配置
	SCHEDULER_EXECUTORS = {
	    'default': {'type': 'threadpool', 'max_workers': 10}
	}

task.py

def my_job():
    print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

view.py

def my_job():
    print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

view.py
"""
添加data类型定时任务
{
	"task_id": "2",
	"run_time": "2020-8-12 12:03:00",
	"trigger_type": "date"
}
添加interval类型定时任务
{
	"task_id": "5",
	"interval_time": 10,
	"trigger_type": "interval"
}
添加cron类型定时任务
{
	"task_id": "5",
	"run_time": {
		"day_of_week": "2",
		"hour": "16",
		"minute": "19",
		"second": "00"
	},
	"trigger_type": "cron"
}
"""
from extensions import scheduler
# 新增job
@app.route('/addCron', methods=['post'])
def add_cron():
    jobargs = request.get_json()
    id = jobargs['task_id']
    trigger_type = jobargs['trigger_type']
    if trigger_type == "date":
        run_time = jobargs['run_time']
        job = scheduler.add_job(func="task:my_job",
                                trigger=trigger_type,
                                run_date=run_time,
                                replace_existing=True,
                                coalesce=True,
                                id=id)
        print("添加一次性任务成功---[ %s ] " % id)
    elif trigger_type == 'interval':
        seconds = jobargs['interval_time']
        seconds = int(seconds)
        if seconds <= 0:
            raise TypeError('请输入大于0的时间间隔!')
        scheduler.add_job(func="task:my_job",
                          trigger=trigger_type,
                          seconds=seconds,
                          replace_existing=True,
                          coalesce=True,
                          id=id)
    elif trigger_type == "cron":
        day_of_week = jobargs["run_time"]["day_of_week"]
        hour = jobargs["run_time"]["hour"]
        minute = jobargs["run_time"]["minute"]
        second = jobargs["run_time"]["second"]
        scheduler.add_job(func="task:my_job", id=id, trigger=trigger_type, day_of_week=day_of_week,
                          hour=hour, minute=minute,
                          second=second, replace_existing=True)
        print("添加周期执行任务成功任务成功---[ %s ] " % id)
    return jsonify(msg="新增任务成功")

# 暂停
@app.route('/<task_id>/pause',methods=['GET'])
def pause_job(task_id):
    response = {'status': False}
    try:
        scheduler.pause_job(task_id)
        response['status'] = True
        response['msg'] = "job[%s] pause success!" % task_id
    except Exception as e:
        response['msg'] = str(e)
    return jsonify(response)

#启动
@app.route('/<task_id>/resume',methods=['GET'])
def resume_job(task_id):
    response = {'status': False}
    try:
        scheduler.resume_job(task_id)
        response['status'] = True
        response['msg'] = "job[%s] resume success!" % task_id
    except Exception as e:
        response['msg'] = str(e)
    return jsonify(response)

#删除
@app.route('/<task_id>/remove',methods=['GET'])
def resume_job(task_id):
    response = {'status': False}
    try:
        scheduler.remove_job(task_id)
        response['status'] = True
        response['msg'] = "job[%s] remove success!" % task_id
    except Exception as e:
        response['msg'] = str(e)
    return jsonify(response)

#编辑
编辑逻辑与新增大致相同,编辑时如果传的task_id 任务表中已存在,那么会直接替换原来的task_id。 

#查job信息,获取的信息包括了job类型和执行时间,可以打印出来结果再根据自己的代码逻辑进行编写
#查看所有的job信息
ret_list = scheduler.get_jobs()
# 查看指定的job信息
ret_list = scheduler.get_job(jid)

定时任务重复启动解决办法

注意:
如果flask用多进程进行部署,定时任务会重复启动,解决办法:在启动任务时,设置文件锁,当能获取到文件锁时,不在启动任务:

def create_app():
    app =Flask(__name__)
    # 启动定时任务
    scheduler_init(app)
    return app

def scheduler_init(app):
    """
    保证系统只启动一次定时任务
    :param app:
    :return:
    """
    if platform.system() != 'Windows':
        fcntl = __import__("fcntl")
        f = open('scheduler.lock', 'wb')
        try:
            fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
            scheduler.init_app(app)
            scheduler.start()
            app.logger.debug('Scheduler Started,---------------')
        except:
            pass

        def unlock():
            fcntl.flock(f, fcntl.LOCK_UN)
            f.close()

        atexit.register(unlock)
    else:
        msvcrt = __import__('msvcrt')
        f = open('scheduler.lock', 'wb')
        try:
            msvcrt.locking(f.fileno(), msvcrt.LK_NBLCK, 1)
            scheduler.init_app(app)
            scheduler.start()
            app.logger.debug('Scheduler Started,----------------')
        except:
            pass

        def _unlock_file():
            try:
                f.seek(0)
                msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, 1)
            except:
                pass

        atexit.register(_unlock_file)

在任务中获取程序上文进行操作

总的来说就是在extensions.py中生成scheduler,在settings.py中配置scheduler,然后在__init__.py中启动scheduler服务,task.py文件中引入extensions.py中的scheduler,然后在任务函数中使用with scheduler.app.app_context()环境

from extensions import scheduler
def task1():
	with scheduler.app.app_context():
	 	#数据库操作
		CronTab.query.filter_by(cron_task_id=datas["task_id"]).first()

 

### 回答1: 可以使用Flask-APScheduler在MongoDB中实现定时任务,只需要在Flask应用中定义一个定时任务,并将其配置为在MongoDB中运行。具体代码如下:from flask_apscheduler import APSchedulerscheduler = APScheduler()# Configure the scheduler to use MongoDB as its job store scheduler.add_jobstore('mongodb', host='localhost', database='your_database_name')@scheduler.task('interval', id='do_job_1', seconds=30) def job_1(): print("Job 1 executed")@scheduler.task('cron', id='do_job_2', day_of_week='mon-sun', hour='12', minute='30') def job_2(): print("Job 2 executed")# Start the scheduler scheduler.start() ### 回答2: 要使用Flask-APScheduler实现数据库MongoDB定时任务,首先需要安装Flask-APScheduler和pymongo库。在Flask应用程序中,可以使用以下代码实现: 1. 首先,在app.py文件中导入所需的模块和库: ```python from flask import Flask from flask_apscheduler import APScheduler from pymongo import MongoClient ``` 2. 创建Flask应用程序实例: ```python app = Flask(__name__) ``` 3. 配置MongoDB连接并创建MongoDB客户端: ```python app.config['MONGO_URI'] = 'mongodb://localhost:27017/db_name' mongo_client = MongoClient(app.config['MONGO_URI']) ``` 请注意,`db_name`应替换为你的实际数据库名称,`localhost:27017`应替换为你的MongoDB服务器地址和端口。 4. 初始化APScheduler实例并配置任务存储: ```python scheduler = APScheduler() scheduler.init_app(app) scheduler.start() ``` 5. 创建一个定时任务函数,该函数将执行需要定时执行的操作。这里以向MongoDB数据库中插入一条记录为例: ```python def insert_data(): db = mongo_client.db_name collection = db.collection_name data = {'name': 'John', 'age': 30} collection.insert_one(data) ``` 请注意,`db_name`和`collection_name`应替换为你的实际数据库和集合名称。 6. 创建一个定时任务,并将其添APScheduler中: ```python scheduler.add_job(func=insert_data, trigger='interval', seconds=60) ``` 这将每隔60秒执行一次`insert_data`函数。 7. 最后,在Flask应用程序的入口处,启动Flask应用程序: ```python if __name__ == '__main__': app.run() ``` 以上代码片段演示了如何使用Flask-APScheduler和pymongo库实现数据库MongoDB定时任务的基本步骤。根据实际需求,你可以根据需要调整设置和任务函数。 ### 回答3: 要使用Flask-APScheduler实现MongoDB数据库的定时任务,需要先安装FlaskFlask-APScheduler库,并且确保MongoDB数据库已经正确安装和配置。 首先,在Flask应用中导入所需要的库和模块: ```python from flask import Flask from flask_apscheduler import APScheduler from pymongo import MongoClient ``` 然后,创建Flask应用和APScheduler实例并配置MongoDB数据库的连接: ```python app = Flask(__name__) scheduler = APScheduler() scheduler.init_app(app) # 配置MongoDB数据库连接 client = MongoClient('mongodb://localhost:27017/') # 替换为实际的MongoDB连接地址 db = client['mydatabase'] # 替换为实际的数据库名称 ``` 接下来,创建一个定时任务函数,该函数在特定时间间隔内会被调度执行,并且可以在函数中访问MongoDB数据库: ```python @scheduler.task('interval', id='my_job', minutes=30) def my_task(): collection = db['mycollection'] # 替换为实际的集合名称 # 在此处添需要执行的MongoDB操作,例如插入、更新、删除等 # 例如:collection.insert_one({"name": "example"}) ``` 最后,启动定时任务调度器和Flask应用: ```python @app.route('/') def index(): return 'Flask-APScheduler MongoDB Demo' if __name__ == '__main__': scheduler.start() app.run() ``` 启动应用后,定时任务会按照预定的时间间隔执行,并且可以在`my_task()`函数中进行MongoDB的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值