Flask-Apscheduler

1. 简介

Flask-APScheduler 
	适配于Flask框架 是APScheduler扩展 
	支持flask配置类加载定时任务调度器配置和任务配置明细
	提供内部restful风格API监控管理定时任务、认证机制、host白名单访问机制
	高度与flask蓝图集成

2. 安装

pip install Flask-APScheduler -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

3. 基础使用

# -*- coding:utf-8 -*-
import datetime

from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from flask import Flask
from flask_apscheduler import APScheduler


class Config:
    SCHEDULER_API_ENABLED = True  # 开启内置API访问权限
    WERKZEUG_RUN_MAIN = True


app = Flask(__name__)  # webApp实例
app.config.from_object(Config())  # 通过配置类配置


scheduler = APScheduler()  

# scheduler.api_enabled = True  # 等价于 类属性 SCHEDULER_API_ENABLED = True
scheduler.init_app(app)
scheduler.start()


# interval触发器实例
@scheduler.task('interval', id='do_job_1', seconds=30, misfire_grace_time=900)
def job1():
    print('Job 1 executed ' + str(datetime.datetime.now()))


# cron触发器实例
@scheduler.task('cron', id='do_job_2', minute='*')
def job2():
    print('Job 2 executed ' + str(datetime.datetime.now()))
    
    
# date触发器实例
def job3():
    print('Job 3 executed ' + str(datetime.datetime.now()))


if __name__ == '__main__':
    scheduler.add_job(id='do_job_3', func=job3, trigger='date', run_date='2023-03-09 15:39:00', timezone="Asia/Shanghai", replace_existing=True)
    # scheduler.add_listener(job3, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
    app.run(use_reloader=False)

4. 组件介绍

triggers>触发器
job stores>任务存储器
executors>执行器
scheduler>调度器

4.1 triggers(触发器)

1. 描述
	任务的运行周期;每个任务都有自己的触发器,只有满足触发条件才会执行任务;可结合多种触发器同时使用

2. 方式
2.1 date 特定时间节点
	参数:
    	run_date: 特定日期 str | date | datetime
    	time_zone: 时区 str
    
2.2 interval 固定时间间隔
	参数:
    	weeks:间隔几周 int
        days:间隔几天 int
        hours:间隔几小时 int
        minutes:间隔几分钟 int
        seconds:间隔几秒 int
        start_date:开始日期 datetime | str
        end_date:结束日期 datetime | str
        timezone:指定时区 str
	
	
2.3 cron 特定时间周期
	参数:
        year: 年 四位数 int | str
        month: 月 (范围1-12) int | str
        day: 日 (范围1-31) int | str
        week:周 (范围1-53) int | str
        day_of_week: 一周中第几天 (范围0-6 0是周一 6是周日 | mon,tue,wed,thu,fri,sat,sun) int |str
        hour: 时 (范围0-23) (int | str)
        minute: 分 (范围0-59) (int | str)
        second: 秒 (范围0-59) (int | str)
        start_date: 开始日期 (datetime | str)
        end_date: 结束日期 (datetime | str)
        timezone: 指定时区 (datetime | str)
     参数表达式
    	* :  任意值
        */x: 每隔x执行
        x-y: 在x-y区间执行
        x,y,z: 在x y z 特定点执行


2.4 date+interval+cron 组合
    AndTrigger(triggers:list, jitter:int|None)
    OrTrigger(triggers:list, jitter:int|None)
    	-jitter: 最多延迟执行时间(s)
	
import datetime

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.combining import AndTrigger, OrTrigger
from apscheduler.triggers.date import DateTrigger
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.cron import CronTrigger
from flask import Flask
from flask_apscheduler import APScheduler

# 1 date触发器
date_trigger = DateTrigger(run_date="2023-03-09 20:56:00", timezone='Asia/Shanghai')
# 2 interval触发器
interval_trigger = IntervalTrigger(seconds=5, timezone='Asia/Shanghai')
# 3 cron触发器
cron_trigger = CronTrigger(minute="*", timezone='Asia/Shanghai')
# 4 复合触发器 combining_trigger
combining_trigger = AndTrigger([IntervalTrigger(minutes=1, timezone='Asia/Shanghai'),
                                CronTrigger(day_of_week="sat,sun", timezone='Asia/Shanghai')])
# combining_trigger = OrTrigger([CronTrigger(day_of_week='mon', hour=2),
#                                CronTrigger(day_of_week='tue', hour=15)])


def job(trigger_type):
    print(f"type-{trigger_type}-job executed " + str(datetime.datetime.now()))


if __name__ == '__main__':
    app = Flask(__name__)
    scheduler = APScheduler(BackgroundScheduler(timezone='Asia/Shanghai'))
    scheduler.init_app(app)
   
    scheduler.add_job(id='date_trigger', func=job, trigger=date_trigger, args=('date_trigger', ), replace_existing=True)
    scheduler.add_job(id='interval_trigger', func=job, trigger=interval_trigger, args=('interval_trigger', ), replace_existing=True)
    scheduler.add_job(id='cron_trigger', func=job, trigger=cron_trigger, args=('cron_trigger', ), replace_existing=True)
    # ps: AndTrigger存在一定问题
    scheduler.add_job(id='combining_trigger', func=job, trigger=combining_trigger, args=('combining_trigger', ), replace_existing=True)
    scheduler.start()
    app.run(port=5001, use_reloader=False)


4.2 job stores(任务存储器)

1. 描述
	任务信息存储;默认保存到内存,提供多种数据中间件存储方式;
2. 方式
2.1 memory(默认内存)
2.2 mongodb(文档型数据库)
2.3 redis(键值对型数据库)
2.4 sqlalchemy(关系型数据库)

3. ps:
	MongoDBStore name 'mongo'
	SqlalchemyJobStore name 'default' using sqlite
	
# -*- coding:utf-8 -*-
from apscheduler.jobstores.redis import RedisJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore

jobstores = {
    'default': SQLAlchemyJobStore(url="sqlite:///jobs.sqlite")
    }

jobstores = {
    'default': SQLAlchemyJobStore(url="mysql://root:xxx@127.0.0.1:3306/job'"),
    # 若出现源码层面问题 更新依赖包版本比如pymysql
    # 'default': SQLAlchemyJobStore(url="mysql+pymysql://root:xxx@127.0.0.1:3306/cron_job'") # 可自定义tablename
    }


jobstores = {
    'default': MongoDBJobStore(host="127.0.0.1", port=27017, database="cronJob", collection="job")
    }


jobstores = {
    'default': RedisJobStore(host="127.0.0.1",port=6379, db=0, password='')
    }

# 引入配置的方法,在创建类时加入配置项
scheduler = BackgroundScheduler(jobstores=jobstores)


4.3 executors(执行器)

1.描述
	负责任务处理;方式可采用线程池或者进程池,与调度器交互;
	
2. 方式
2.1 threadpoolExecutor
2.2 processpoolExecutor

3. ps
	threadpoolExecutor name 'default' 默认20
	processpoolExecutor name 'processpool' 默认5
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}
sche = BackgroundScheduler(executors=executors)
# 任务配置
job_defaults = {
    # 不合并执行>异常任务再下次服务启动后是否重启 
    'coalesce': False,
    # 同一时间同个任务最大执行次数为3
    'max_instances': 3,
    # 任务错过当前时间60s内,仍然可以触发任务
    'misfire_grace_time':60
}
# 创建类,导入配置
scheduler = BackgroundScheduler(job_defaults=job_defaults)

4.4 scheduler(调度器)

1. 描述
	任务发布决策;调度器与其他组件交互完成任务的添加 修改 删除 查询;

2. 类型
2.1 BlockingScheduler: use when the scheduler is the only thing running in your process(阻塞)

2.2 BackgroundScheduler: use when you’re not using any of the frameworks below, and want the scheduler to run in the background inside your application(非阻塞)

2.3 AsyncIOScheduler: use if your application uses the asyncio module(异步asyncio框架)

2.4 GeventScheduler: use if your application uses gevent(高并发gevent模块)

2.5 TornadoScheduler: use if you’re building a Tornado application(tornado Web框架)

2.6 TwistedScheduler: use if you’re building a Twisted application(twisted 基于reactor异步非阻塞io网络框架)

2.7 QtScheduler: use if you’re building a Qt application(Qt Ui)
	
# -*- coding:utf-8 -*-

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor


# 方式1 scheduler
jobstores = {  # 任务存储器配置
    'mongo': MongoDBJobStore(),
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {  # 任务执行器配置
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}
job_defaults = {  # 任务常规配置
    'coalesce': False,
    'max_instances': 3,
}
scheduler1 = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone="Asia/Shanghai")


# 方式2
scheduler2 = BackgroundScheduler({
    'apscheduler.jobstores.mongo': {
         'type': 'mongodb'
    },
    'apscheduler.jobstores.default': {
        'type': 'sqlalchemy',
        'url': 'sqlite:///jobs.sqlite'
    },
    'apscheduler.executors.default': {
        'class': 'apscheduler.executors.pool:ThreadPoolExecutor',
        'max_workers': '20'
    },
    'apscheduler.executors.processpool': {
        'type': 'processpool',
        'max_workers': '5'
    },
    'apscheduler.job_defaults.coalesce': 'false',  # 是否合并执行任务默认true;false则服务重启后异常未执行的任务会累计重新执行
    'apscheduler.job_defaults.max_instances': '3',  # 同一时间特定任务执行的实例个数;大于1时若之前任务未完成会再满足触发器的情况再次执行该任务
    'apscheduler.timezone': 'UTC',  # 时区
})

# 方式3
jobstores = {
    'mongo': {'type': 'mongodb'},
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': {'type': 'threadpool', 'max_workers': 20},
    'processpool': ProcessPoolExecutor(max_workers=5)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}
scheduler = BackgroundScheduler()
scheduler.configure(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone='Asia/Shanghai')


5. 常规设置

5.1 Schedule配置参数

SCHEDULER_API_ENABLED: bool (default: False)  
SCHEDULER_API_PREFIX: str (default: "/scheduler")  
SCHEDULER_ENDPOINT_PREFIX: str (default: "scheduler.") 
SCHEDULER_ALLOWED_HOSTS: list (default: ["*"]) 
SCHEDULER_JOBSTORES: dict  
SCHEDULER_EXECUTORS: dict
SCHEDULER_JOB_DEFAULTS: dict
SCHEDULER_TIMEZONE: dict
jobstores = {  # 任务存储器配置
    'default': RedisJobStore(host='127.0.0.1', port=6379, db=10, password=''),
    # 'default': SQLAlchemyJobStore(url="mysql://root:xxx@127.0.0.1:3306/job'"),
    # 'default': MongoDBJobStore(host="127.0.0.1", port=27017, database="cronJob", collection="job"),
}
executors = {  # 任务执行器配置
    'default': ThreadPoolExecutor(20),  # 线程池
    'processpool': ProcessPoolExecutor(5)  # 进程池
} 
job_defaults = {  # 任务常规配置
    'coalesce': False,  # True执行一次 False累计异常的任务会在下次重新执行 
    'max_instances': 3, # 同时间同个任务执行的最大实例数
    'misfire_grace_time': 60,  # 任务在触发器满足条件后的60s 还可触发任务
}


5.2 内置API

# 前置配置
SCHEDULER_API_ENABLED: True
# API集合
/scheduler [GET] > returns basic information about the webapp  > 获取服务基本信息

/scheduler/jobs [POST json job data] > adds a job to the scheduler > 新增任务(需要入参 类型json)
{
    "id": "job3",
    "name": "job3",
    "func": "flaskApschedulerTest:job2",
    "args": [
        "cron_test"
    ],
    "trigger": "cron",
    "minute": "*",
    "second": "45",
    "misfire_grace_time": 1,
    "max_instances": 1
}

/scheduler/jobs/<job_id> [GET] > returns json of job details > 查询特定任务信息

/scheduler/jobs [GET] > returns json with details of all jobs > 查询所有任务信息

/scheduler/jobs/<job_id> [DELETE] > deletes job from scheduler > 删除特定任务

/scheduler/jobs/<job_id> [PATCH json job data] > updates an already existing job > 修改特定任务配置信息(需要入参 id参数不传 类型json)
{
    "name": "job3",
    "func": "flaskApschedulerTest:job2",
    "args": [
        "cron_test"
    ],
    "trigger": "cron",
    "minute": "*",
    "second": "45",
    "misfire_grace_time": 1,
    "max_instances": 1
}

/scheduler/jobs/<job_id>/pause [POST] > pauses a job, returns json of job details > 终止特定任务

/scheduler/jobs/<job_id>/resume [POST] > resumes a job, returns json of job details > 唤醒特定任务

/scheduler/jobs/<job_id>/run [POST] > runs a job now, returns json of job details > 立即运行特定任务


5.3 常用方法

scheduler.start() > 调度器运行=运行jobstores的任务

scheduler.shutdown(wait=False) > 默认为true 等待其他任务完成

scheduler.pause() > 停止所有任务 正在运行的不受影响

scheduler.resume() > 唤醒任务

scheduler.add_listener(<callback function>,<event>) > 添加任务监听事件

scheduler.remove_listener(<callback function>) > 移除监听事件

scheduler.add_job(<id>,<function>, **kwargs) > 添加任务

scheduler.remove_job(<id>, **<jobstore>) > 移除任务

scheduler.remove_all_jobs(**<jobstore>) > 移除所有

scheduler.get_job(<id>,**<jobstore>) > 获取任务信息

scheduler.get_jobs() > 获取所有任务信息

scheduler.modify_job(<id>,**<jobstore>, **kwargs) > 修改任务信息

scheduler.pause_job(<id>, **<jobstore>) > 中止任务

scheduler.resume_job(<id>, **<jobstore>) > 唤醒任务

scheduler.run_job(<id>, **<jobstore>) > 运行任务

scheduler.authenticate(<function>) > 认证


6. 实例

6.1 实例1

# -*- coding:utf-8 -*-
"""
pip install Flask-APScheduler
"""
import datetime
import time

from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.redis import RedisJobStore
from apscheduler.schedulers.background import BackgroundScheduler
from flask import Flask

from flask_apscheduler import APScheduler
from flask_apscheduler.auth import HTTPBasicAuth


class Config:
    # 任务集
    JOBS = [
        {
            "id": "job1",  # 任务id
            "func": "flaskApschedulerTest:job1",  # 前面是job所处python文件的地址:后面不能有空字符
            "args": (1, 2),  # 执行函数参数
            "trigger": "interval",  # 触发器
            "seconds": 10,
            'replace_existing': True
        },
        {
            "id": "job2",
            'func': 'flaskApschedulerTest:job2',
            'args': ("cron",),
            "trigger": "cron",
            'minute': "*",
            "second": 15,
            'replace_existing': True
        }
    ]
    
    # 任务信息储存器
    SCHEDULER_JOBSTORES = {
        'default': MongoDBJobStore(host="127.0.0.1", port=27017, database="cronJob", collection="job"),
        # 'default': RedisJobStore(host='127.0.0.1', port=6379, db=10, password='')
    }
    # 执行器配置
    SCHEDULER_EXECUTORS = {"default": {"type": "threadpool", "max_workers": 20}}
    # 执行任务配置
    SCHEDULER_JOB_DEFAULTS = {"coalesce": True, "max_instances": 1}
    # API访问认证
    SCHEDULER_AUTH = HTTPBasicAuth()
    # API访问路径
    SCHEDULER_API_PREFIX = '/scheduler'
    # API访问开启
    SCHEDULER_API_ENABLED = True
    # 时区
    SCHEDULER_TIMEZONE = 'Asia/Shanghai'
    # HOST白名单
    SCHEDULER_ALLOWED_HOSTS = ["*"]


def job1(var_one, var_two):
    """Demo job function.
    :param var_two:
    :param var_two:
    """
    time.sleep(3)
    print(datetime.datetime.now(), str(var_one) + " " + str(var_two))
    

def job2(name):
    print(datetime.datetime.now(), f" {name}")


if __name__ == "__main__":
    app = Flask(__name__)
    app.config.from_object(Config())

    scheduler = APScheduler(BackgroundScheduler(timezone='Asia/Shanghai'))
    scheduler.init_app(app)
    scheduler.start()


    @scheduler.authenticate
    def authenticate(auth):
        """Check auth"""
        return auth["username"] == "guest" and auth["password"] == "guest"


    # print(scheduler.get_jobs())
    app.run(host='0.0.0.0', port=5001, use_reloader=False)

6.1 实例2

#--cron
#--cron/task/task1.py
#--app.py
#--config.py
#--__init__.py

# task1.py
import datetime
import time
from multiprocessing import Process


def task_1(name):
    time.sleep(10)
    print(f"{name} running " + str(datetime.datetime.now()))
    
    
def run():
    pl = [Process(target=task_1, args=(str(s) + "_task1", )) for s in range(3)]
    for p in pl:
        p.start()
    for p in pl:
        p.join()
    print(">>>>执行下面的进程了1")
    p = Process(target=task_1, args=("my1", ))
    p.start()
    p.join()
    print(">>>>执行下面的进程了2")
    p = Process(target=task_1, args=("my2",))
    p.start()
    p.join()
    
#__init__.py
import atexit
import platform

from apscheduler.schedulers.background import BackgroundScheduler
from flask_apscheduler import APScheduler
from config import Config

scheduler = APScheduler(BackgroundScheduler(timezone='Asia/Shanghai'))


def create_app(config=Config):
    from flask import Flask
    app = Flask(__name__)
    app.config.from_object(config)
    scheduler_init(app)
    return app


def scheduler_init(app):
    """
    保证系统只启动一次定时任务
    :param app: 当前flask实例
    :return: None
    """
    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()
        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()
        except:
            pass

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

        atexit.register(_unlock_file)
# config.py
from apscheduler.jobstores.redis import RedisJobStore


class Config:
    # 任务集
    JOBS = [
        {
            "id": "my_job1",  # 任务id
            "func": "web.cron.task.task1:run",
            "trigger": "interval",
            "seconds": 1,
            'replace_existing': True,
        },
        {
            "id": "my_job2",  # 任务id
            "func": "web.cron.task.task1:task_1",
            "trigger": "interval",
            "args": ("my_job2", ),
            "seconds": 3,
            'replace_existing': True,
        }
    ]
    
    # 任务信息储存器
    SCHEDULER_JOBSTORES = {
        # 'default': MongoDBJobStore(host="127.0.0.1", port=27017, database="cronJob", collection="job"),
        'default': RedisJobStore(host='127.0.0.1', port=6379, db=10, password=''),
        # 'default': SQLAlchemyJobStore(url="mysql://root:python@localhost:3306/cron_job?charset=utf8"),
    }
    # 执行器配置
    SCHEDULER_EXECUTORS = {"default": {"type": "threadpool", "max_workers": 20}}
    # 执行任务配置
    SCHEDULER_JOB_DEFAULTS = {"coalesce": True, "max_instances": 1}
    # API访问路径
    SCHEDULER_API_PREFIX = '/scheduler'
    # API访问开启
    SCHEDULER_API_ENABLED = True
    # 时区
    SCHEDULER_TIMEZONE = 'Asia/Shanghai'
    # HOST白名单
    SCHEDULER_ALLOWED_HOSTS = ["*"]
    
# app.py
from web.cron import create_app

app = create_app()

if __name__ == '__main__':
    app.run(use_reloader=False)


7. 参考文档

[1] flask-apscheduler https://pypi.org/project/APScheduler/

[2] [风潇逸Oo] https://www.jianshu.com/p/4c5bc85fc3fd

posted @ 2023-03-13 19:56  爱编程_喵  阅读(738)  评论(0编辑  收藏  举报
jQuery火箭图标返回顶部代码

jQuery火箭图标返回顶部代码

滚动滑动条后,查看右下角查看效果。很炫哦!!

适用浏览器:IE8、360、FireFox、Chrome、Safari、Opera、傲游、搜狗、世界之窗.