展开

文章目录

修改历史

修改历史记录

  1. 2020-12-08 13:42:24

使用flask-apscheduler控制定时任务遇到的问题

2020-12-01 22:42:12 Python 1001

简介

很多时候,由于我对每一个框架不是十分的熟悉,我们在使用的过程中都会或多或少遇到这样或者那样的问题,在使用flask-apscheduler的过程中,我同样也遇到了一些坑,在这里说明一下并总结一下解决方案。

定时任务不启动的问题

在我使用flask-apscheluder进行测试的时候,我发现定时任务不会启动,我的应用采用的启动方式是命令行模式,同时使用的是工厂方法去创建实例。但是在我使用flask run来启动了应用,定时任务并没有定时执行。

我运行flask-aspscheduler文档中的示例代码的时候,定时任务会按照我设定的值进行启动,为什么我使用flask run去启动的时候,不按照我预设的值去执行定时任务呢?

经过一番摸索之后,我发现需要将当前启动环境设置为production模式,即生产模式。如果你使用的.flaskenv文件去配置的话,可以将FLASK_ENV参数设置为production 。

FLASK_ENV=production

如果你使用的export的方式

export FLASK_ENV=production

再次运行,定时任务成功启动了。这个不知道是bug还是什么问题,没有细看flask-aspscheluder的文档。

在定时任务中进行数据库操作

我原本的目的就是想在定时任务中执行数据库操作,在每天的零点零五分在数据库中插入一条数据。一开始没有仔细看官方给的示例,死活使用不了,后来看了官方的示例文档,发现如果要操作数据库,需要通过如下方式执行

@aps.task('cron', id='do_job_3', day='*', hour='00', minute='00', second='50')
def auto_insert_data():
    """
    定时任务,每天00:00:50时刻自动向数据库中插入一条数据,如果数据库中存在了则不作任何动作
    """
    with db.app.app_context():
        date = datetime.date.today()
        contribute = Contribute.query.filter_by(date=date).first()
        visit = VisitStatistics.query.filter_by(date=date).first()
        lk = LikeStatistics.query.filter_by(date=date).first()
        com = CommentStatistics.query.filter_by(date=date).first()

        if not contribute:
            con = Contribute(contribute_counts=0, date=date)
            db.session.add(con)
        if not visit:
            vis = VisitStatistics(date=date, times=0)
            db.session.add(vis)
        if not lk:
            like = LikeStatistics(date=date, times=0)
            db.session.add(like)
        if not com:
            comm = CommentStatistics(date=date, times=0)
            db.session.add(comm)
        db.session.commit()

使用gunicorn多进程部署问题

当我在开发环境中测试通过时,我把更新过后的代码推送到服务器上去了。第二天发现了问题,同时在数据库中插入了六条数据。一时半会想不明白是什么原因,然后google了一会儿,很多人也遇到过同样的问题,大致意思就是gunicorn开启的进程个数有多个少个,就会启动多少个定时任务。我的gunicorn开了六个进程,所以启动了六个定时任务,向数据库中插入了六条数据。

解决办法

# __init__.py 文件

def scheduler_init(app):
    """
    保证系统只启动一次定时任务
    :param app: 当前flask实例
    :return: None
    """
    if platform.system() != 'Windows':
        fcntl = __import__("fcntl")
        f = open(basedir+'/scheduler.lock', 'wb')
        try:
            fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
            aps.init_app(app)
            aps.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(basedir+'scheduler.lock', 'wb')
        try:
            msvcrt.locking(f.fileno(), msvcrt.LK_NBLCK, 1)
            aps.init_app(app)
            aps.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)

在你的工厂函数中调用该方法,就可以避免同时启动多个定时任务啦!

0条评论