flask+APScheduler定时任务的使用

APScheduler定时任务使用以及在flask中的调用

  • APScheduler简介
    • 组成部分
      • 调度器
    • 安装
      • 普通使用安装
      • 结合flask使用安装
  • 使用
    • 添加job
    • add_job参数详解
      • interval 间隔时间(每隔一段时间执行)
      • date 定时调度(只执行一次)
      • cron定时调度(某一定时时刻执行)
    • job的其他操作
  • 结合flask使用(大众点评数据同步实例)
    • 在flask的config配置中添加:
    • 在main.py文件创建flask实例并初始化APScheduler

任务需求:在flask中每天自动同步大众点评数据到数据库,这里选择了Flask-APScheduler

APScheduler简介

APScheduler基于Quartz的一个Python定时任务框架,实现了Quartz的所有功能,使用起来十分方便。提供了基于日期、固定时间间隔以及crontab类型的任务,并且可以持久化任务。基于这些功能,我们可以很方便的实现一个python定时任务系统。

组成部分

APScheduler有四种组成部分:

  • 触发器(trigger)包含调度逻辑,每一个作业有它自己的触发器,用于决定接下来哪一个作业会运行。除了他们自己初始配置意外,触发器完全是无状态的。
  • 作业存储(jobstore)存储被调度的作业,默认的作业存储是简单地把作业保存在内存中,其他的作业存储是将作业保存在数据库中。一个作业的数据讲在保存在持久化作业存储时被序列化,并在加载时被反序列化。调度器不能分享同一个作业存储。
  • 执行器(executor)处理作业的运行,他们通常通过在作业中提交制定的可调用对象到一个线程或者进城池来进行。当作业完成时,执行器将会通知调度器。
  • 调度器(scheduler)是其他的组成部分。你通常在应用只有一个调度器,应用的开发者通常不会直接处理作业存储、调度器和触发器,相反,调度器提供了处理这些的合适的接口。配置作业存储和执行器可以在调度器中完成,例如添加、修改和移除作业。

调度器

APScheduler提供了多种调度器,可以根据具体需求来选择合适的调度器,常用的调度器有:

  • BlockingScheduler:适合于只在进程中运行单个任务的情况,通常在调度器是你唯一要运行的东西时使用。

  • BackgroundScheduler: 适合于要求任何在程序后台运行的情况,当希望调度器在应用后台执行时使用。

  • AsyncIOScheduler:适合于使用asyncio框架的情况

  • GeventScheduler: 适合于使用gevent框架的情况

  • TornadoScheduler: 适合于使用Tornado框架的应用

  • TwistedScheduler: 适合使用Twisted框架的应用

  • QtScheduler: 适合使用QT的情况

安装

普通使用安装

pip install apscheduler

结合flask使用安装

pip install Flask-APScheduler

使用

添加job

  1. work1使用add_job()方法添加,表示每隔5秒执行一次,第一次执行是程序运行5秒后
  2. work2使用装饰器添加
import time
from apscheduler.schedulers.blocking import BlockingScheduler

sched = BlockingScheduler()


def work1():
    print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))


@sched.scheduled_job('interval', seconds=2)
def work2():
    print('work2222')

sched.add_job(work1, 'interval', seconds=5)
sched.start()

add_job参数详解

id

# id代表该job唯一标识,不可重复,之后可以用id查找job
sched.add_job(work1, 'interval', seconds=5, id='my_work1')

trigger
它管理着作业的调度方式。它可以为date, interval或者cron。对于不同的trigger,对应的参数也不同。

interval 间隔时间(每隔一段时间执行)

后面可以写的参数:

  • weeks (int) – number of weeks to wait
  • days (int) – number of days to wait
  • hours (int) – number of hours to wait
  • minutes (int) – number of minutes to wait
  • seconds (int) – number of seconds to wait
  • start_date (datetime|str) – starting point for the interval
    calculation
  • end_date (datetime|str) – latest possible date/time to trigger on
  • timezone (datetime.tzinfo|str) – time zone to use for the date/time
    calculations
#表示每隔3天17时19分07秒执行一次任务
sched.add_job(my_job, 'interval',days=3,hours=17,minutes=19,seconds=7)

date 定时调度(只执行一次)

  • run_date (datetime|str) – the date/time to run the job at -(任务开始的时间)
  • timezone (datetime.tzinfo|str) – time zone for run_date if it doesn’t have one already
# The job will be executed on November 6th, 2009
sched.add_job(func=work1, trigger='date', run_date=date(2009, 11, 6), args=['text'])
# The job will be executed on November 6th, 2009 at 16:30:05
sched.add_job(func=work1, trigger='date', run_date=datetime(2009, 11, 6, 16, 30, 5), args=['text'])

cron定时调度(某一定时时刻执行)

后面参数和interval大致相同

# 表示2017年3月22日17时19分07秒执行该程序
sched.add_job(work1, 'cron', year=2017, month=3, day=22, hour=17, minute=19, second=7)
# 表示任务在6,7,8,11,12月份的第三个星期五的00:00,01:00,02:00,03:00 执行该程序
sched.add_job(work1, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')
# 表示从星期一到星期五5:30(AM)直到2014-05-30 00:00:00
sched.add_job(work1, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2014-05-30')
# 表示每5秒执行该程序一次,相当于interval 间隔调度中seconds = 5
sched.add_job(work1, 'cron', second='*/5')

args 参数,要可迭代对象

job的其他操作

# 移除所有,移除要放在start之前才有效
sched.remove_all_jobs()
# 根据id移除job
sched.remove_job('my_work1')
sched.pause_job('my_work1')  # 暂停
sched.resume_job('my_work1')  # 恢复
sched.get_job('my_work1')  # 获取
sched.get_jobs()  # 获取所有job列表
# 默认情况是调度器等所有job完成后关闭,设为False时直接关闭
sched.shutdown(wait=False)  

结合flask使用(大众点评数据同步实例)

flask的项目结构是util目录下有个dianpingUtil.py文件,文件里有个函数dianPing()是要执行的定时任务

import hashlib
import time
import json
from http import client
from urllib.parse import urlencode
from model.ZLHModel import ZlhUserFinanceDianping


def dianPing(current_app):
    data_type = 1

    today_date = time.strftime("%Y-%m-%d", time.localtime())
    _where = (ZlhUserFinanceDianping.ufd_date == today_date) & (ZlhUserFinanceDianping.ufd_data_type == data_type)
    # 如果已存在则不访问
    user_finance_dianping_count = ZlhUserFinanceDianping.select().where(_where).count()
    if not user_finance_dianping_count:
        host = 'openapi.dianping.com'
        appsecret = current_app.config['DIANPING_APPSECRET']
        time_stamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        params = {'app_key': current_app.config['DIANPING_APPKEY'], 'timestamp': time_stamp,
                  'session': current_app.config['DIANPING_SESSION'], 'format': "json", 'v': "1",
                  'sign_method': "MD5",
                  'open_shop_uuid': current_app.config['DIANPING_OPEN_SHOP_UUID'], 'date_type': str(data_type)}
        signstr = sign(params, appsecret, 'MD5')
        params['sign'] = signstr
        encode_params = urlencode(params)
        request_url = "https://openapi.dianping.com/router/merchant/data/consumption"
        header = {"Content-type": "application/x-www-form-urlencoded ; charset=UTF-8"}
        conn = client.HTTPConnection(host)
        conn.request(method="POST", url=request_url, headers=header, body=encode_params)
        response = conn.getresponse()
        res = response.read()
        result = json.loads(res)
        if result.get('code') == 200:
            for data in result.get('data'):
                ZlhUserFinanceDianping.create(ufd_tg_consume_amount=data.get('tg_consume_amount') * 100,
                                              ufd_tg_consume_count=data.get('tg_consume_count'),
                                              ufd_mopay_consume_amount=data.get('mopay_consume_amount') * 100,
                                              ufd_mopay_consume_count=data.get('mopay_consume_count'),
                                              ufd_reservation_consume_amount=data.get(
                                                  'reservation_consume_amount') * 100,
                                              ufd_reservation_consume_count=data.get('reservation_consume_count'),
                                              ufd_platform=data.get('platform'),
                                              ufd_date=today_date, ufd_data_type=data_type,
                                              )
            current_app.logger.info('大众点评完成同步')
        else:
            current_app.logger.info('大众点评返回结果错误' + result.get('msg'))
    else:
        current_app.logger.info('点评当日已保存')


def sign(param, appsecret, signmethod):

    if signmethod !="MD5":
        return ''

    lists = []
    param_str = appsecret
    for item in param:
        lists.append(item)

    lists.sort()

    for key in lists:
        param_str = param_str + key + param[key]

    param_str += appsecret
    param_str = param_str.strip()

    return genMd5(param_str)


def genMd5(str):
    md5 = hashlib.md5()
    md5.update(str.encode("utf8"))
    md5.hexdigest()

    return md5.hexdigest()

在flask的config配置中添加:

SCHEDULER_API_ENABLED = True
# 添加需要的配置
# 大众点评
DIANPING_APPSECRET = '***'
DIANPING_APPKEY = '***'
DIANPING_OPEN_SHOP_UUID = '***'
DIANPING_SESSION = '***'
DIANPING_REFRESH_SESSION = '**'

在main.py文件创建flask实例并初始化APScheduler

这里的重点是定时任务需要用到flask app上下文,所以直接将app作为参数传过去即可

from flask import Flask
from flask_apscheduler import APScheduler
...  # 省略导入配置文件

app = Flask(__name__, template_folder='templates', static_url_path='/static')
# 注册APScheduler,添加任务
scheduler = APScheduler()
scheduler.init_app(app)
# 表示一天后开始执行,然后每天执行一次
scheduler.add_job(id='dianping', func='util.DianpingUtil:dianPing',
                  trigger='interval', day=1, args=[app, ])
scheduler.start()
app.config.from_object(app_config[‘dev’])  # 配置文件
# 省略
...

你可能感兴趣的:(Flask)