基于多进程中APScheduler重复运行的解决方法


Posted in Python onJuly 22, 2019

问题

在一个python web应用中需要定时执行一些任务,所以用了APScheduler这个库。又因为是用flask这个web框架,所以用了flask-apscheduler这个插件(本质上与直接用APScheduler一样,这里不作区分)。

在开发中直接测试运行是没有问题的,但是用gunicorn部署以后发生了重复运行的问题:

每个任务在时间到的时刻会同时执行好几遍。

注意了一下重复的数量,恰恰是gunicorn里配置的worker进程数量,显然是每个worker进程都启动了一份scheduler造成。

解决

可以想到的方案有几个:

用--preload启动gunicorn,确保scheduler只在loader的时候创建一次

另外创建一个单独的定时任务项目,单独以一个进程运行

用全局锁确保scheduler只运行一次

经过实践,只有第三个方案比较好。

preload的问题:

虽然这样可以使用scheduler创建代码只执行一次,但是问题也在于它只执行一次,重新部署以后如果用kill -HUP重启gunicorn,它并不会重启,甚至整个项目都不会更新。这是preload的副作用,除非重写部署脚本,完全重启应用。

单独进程的问题:

也是因为部署麻烦,需要多一套部署方案,虽然用Docker会比较方便,但仍然不喜欢,而且同时维护两个项目也多出很多不必要的事情。

全局锁是一个较好的方案,但问题在于找一个合适的锁。

python自带的多进程多线程锁方案都需要一个共享变量来维护,但是因为worker进程是被gunicorn的主进程启动的,并不方便自己维护,所以需要一个系统级的锁。

在Stackoverflow上看到有人是用了一个socket端口来做锁实现这个方案,但是我也不喜欢这样浪费一个宝贵的端口资源。不过这倒给了我一个启发:

可以用文件锁!

于是有了这个解决方案:

import atexit
import fcntl
from flask_apscheduler import APScheduler

def init(app):
 f = open("scheduler.lock", "wb")
 try:
  fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
  scheduler = APScheduler()
  scheduler.init_app(app)
  scheduler.start()
 except:
  pass
 def unlock():
  fcntl.flock(f, fcntl.LOCK_UN)
  f.close()
 atexit.register(unlock)

原理

init函数为flask项目初始化所调用,这里为scheduler模块的初始化部分。

首先打开(或创建)一个scheduler.lock文件,并加上非阻塞互斥锁。成功后创建scheduler并启动。

如果加文件锁失败,说明scheduler已经创建,就略过创建scheduler的部分。

最后注册一个退出事件,如果这个flask项目退出,则解锁并关闭scheduler.lock文件的锁。

以上这篇基于多进程中APScheduler重复运行的解决方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python中正则表达式的使用详解
Oct 17 Python
python实现多线程暴力破解登陆路由器功能代码分享
Jan 04 Python
Python中的rfind()方法使用详解
May 19 Python
结合Python的SimpleHTTPServer源码来解析socket通信
Jun 27 Python
python3实现公众号每日定时发送日报和图片
Feb 24 Python
python 读取目录下csv文件并绘制曲线v111的方法
Jul 06 Python
Python交互环境下打印和输入函数的实例内容
Feb 16 Python
Python unittest框架操作实例解析
Apr 13 Python
python操作redis数据库的三种方法
Sep 10 Python
Python requests HTTP验证登录实现流程
Nov 05 Python
python小型的音频操作库mp3Play
Apr 24 Python
Django框架中模型的用法
Jun 10 Python
django云端留言板实例详解
Jul 22 #Python
python实现图片中文字分割效果
Jul 22 #Python
django用户登录验证的完整示例代码
Jul 21 #Python
Python Threading 线程/互斥锁/死锁/GIL锁
Jul 21 #Python
详解Django模版中加载静态文件配置方法
Jul 21 #Python
django数据库自动重连的方法实例
Jul 21 #Python
django使用django-apscheduler 实现定时任务的例子
Jul 20 #Python
You might like
php 删除记录实现代码
2009/03/12 PHP
PHP+ajax分页实例简析
2015/12/07 PHP
用HTML/JS/PHP方式实现页面延时跳转的简单实例
2016/07/18 PHP
Yii2简单实现多语言配置的方法
2016/07/23 PHP
windows7配置Nginx+php+mysql的详细教程
2016/09/04 PHP
简单实现PHP留言板功能
2016/12/21 PHP
实例讲解PHP页面静态化
2018/02/05 PHP
php模式设计之观察者模式应用实例分析
2019/09/25 PHP
AJAX架构之Dojo篇
2007/04/10 Javascript
JavaScript事件处理器中的event参数使用介绍
2013/05/24 Javascript
Js判断CSS文件加载完毕的具体实现
2014/01/17 Javascript
javascript实现跨域的方法汇总
2015/06/25 Javascript
jQuery插件jquery-barcode实现条码打印的方法
2015/11/25 Javascript
AngularJS实践之使用ng-repeat中$index的注意点
2016/12/22 Javascript
js实现九宫格的随机颜色跳转
2017/02/19 Javascript
JavaScript中Object基础内部方法图
2018/02/05 Javascript
antd多选下拉框一行展示的实现方式
2020/10/31 Javascript
[01:52]PWL S2开团时刻第四期——DOTA2成语故事
2020/12/03 DOTA
python创建临时文件夹的方法
2015/07/06 Python
python实现的二叉树定义与遍历算法实例
2017/06/30 Python
Python3实现的旋转矩阵图像算法示例
2019/04/03 Python
python实现爬取百度图片的方法示例
2019/07/06 Python
django框架中ajax的使用及避开CSRF 验证的方式详解
2019/12/11 Python
jupyter修改文件名方式(TensorFlow)
2020/04/21 Python
python 发送邮件的示例代码(Python2/3都可以直接使用)
2020/12/03 Python
Pandas的数据过滤实现
2021/01/15 Python
Android面试宝典
2013/08/06 面试题
Lucene推荐的分页方式是什么?
2015/12/07 面试题
求职简历推荐信范文
2013/12/02 职场文书
真诚的求职信
2014/07/04 职场文书
技术股份合作协议书
2014/10/05 职场文书
2014年设备管理工作总结
2014/11/26 职场文书
2015年双拥工作总结
2015/04/08 职场文书
导游词之重庆钓鱼城
2019/09/19 职场文书
《分一些蚊子进来》读后感3篇
2020/01/09 职场文书
Windows Server 2012 R2 磁盘分区教程
2022/04/29 Servers