基于多进程中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删除nginx缓存文件示例(python文件操作)
Mar 26 Python
老生常谈python之鸭子类和多态
Jun 13 Python
python实现员工管理系统
Jan 11 Python
关于Python正则表达式 findall函数问题详解
Mar 22 Python
python根据url地址下载小文件的实例
Dec 18 Python
Python 保存矩阵为Excel的实现方法
Jan 28 Python
Python3爬虫之自动查询天气并实现语音播报
Feb 21 Python
pandas DataFrame 行列索引及值的获取的方法
Jul 02 Python
在windows下使用python进行串口通讯的方法
Jul 02 Python
python或C++读取指定文件夹下的所有图片
Aug 31 Python
提升python处理速度原理及方法实例
Dec 25 Python
pytorch 实现删除tensor中的指定行列
Jan 13 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 date与gmdate的获取日期的区别
2010/02/08 PHP
PHP中strcmp()和strcasecmp()函数字符串比较用法分析
2016/01/07 PHP
在laravel中实现将查询的对象转换为多维数组的函数
2019/10/21 PHP
use jscript with List Proxy Server Information
2007/06/11 Javascript
通过event对象的fromElement属性解决热区设置主实体的一个bug
2008/12/22 Javascript
javascript window对象属性整理
2009/10/24 Javascript
鼠标移到导航当前位置的LI变色处于选中状态
2013/08/23 Javascript
js截取小数点后几位的写法
2013/11/14 Javascript
在JavaScript中操作时间之getUTCDate()方法的使用
2015/06/10 Javascript
js仿黑客帝国字母掉落效果代码分享
2020/11/08 Javascript
js时间比较 js计算时间差的简单实现方法
2016/08/26 Javascript
JavaScript 是什么意思
2016/09/22 Javascript
Radio 单选JS动态添加的选项onchange事件无效的解决方法
2016/12/12 Javascript
详解JavaScript 中getElementsByName在IE中的注意事项
2017/02/21 Javascript
浅谈js for循环输出i为同一值的问题
2017/03/01 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
详谈jQuery.load()和Jsp的include的区别
2017/04/12 jQuery
关于vue面试题汇总
2018/03/20 Javascript
200行HTML+JavaScript实现年会抽奖程序
2019/01/22 Javascript
vue+php实现的微博留言功能示例
2019/03/16 Javascript
基于node简单实现RSA加解密的方法步骤
2019/03/21 Javascript
8 个有用的JS技巧(推荐)
2019/07/03 Javascript
详解Vue中的基本语法和常用指令
2019/07/23 Javascript
浅谈Vue项目骨架屏注入实践
2019/08/05 Javascript
浅谈Node新版本13.2.0正式支持ES Modules特性
2019/11/25 Javascript
[03:32]2014DOTA2西雅图邀请赛 CIS外卡赛赛前black专访
2014/07/09 DOTA
[36:19]2018DOTA2亚洲邀请赛 小组赛 A组加赛 Newbee vs LGD
2018/04/03 DOTA
解决python ogr shp字段写入中文乱码的问题
2018/12/31 Python
Python操作SQLite数据库过程解析
2019/09/02 Python
Turnbull & Asser官网:英国皇室御用的顶级定制衬衫
2019/01/31 全球购物
Viking Direct荷兰:购买办公用品
2019/06/20 全球购物
JYSK加拿大:购买家具、床垫、家居装饰等
2020/02/14 全球购物
全球性的众包图形设计市场:DesignCrowd
2021/02/02 全球购物
比较一下entity bean和session bean
2013/12/27 面试题
经管应届生求职信范文
2014/05/18 职场文书
机关党员进社区活动总结
2014/07/05 职场文书