基于多进程中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 相关文章推荐
Django中实现一个高性能计数器(Counter)实例
Jul 09 Python
Python生成随机数组的方法小结
Apr 15 Python
Python中的pygal安装和绘制直方图代码分享
Dec 08 Python
django 开发忘记密码通过邮箱找回功能示例
Apr 17 Python
django.db.utils.ProgrammingError: (1146, u“Table‘’ doesn’t exist”)问题的解决
Jul 13 Python
tensorflow 恢复指定层与不同层指定不同学习率的方法
Jul 26 Python
Python使用线程来接收串口数据的示例
Jul 02 Python
python处理自动化任务之同时批量修改word里面的内容的方法
Aug 23 Python
pytorch 使用加载训练好的模型做inference
Feb 20 Python
浅谈keras通过model.fit_generator训练模型(节省内存)
Jun 17 Python
pandas apply使用多列计算生成新的列实现示例
Feb 24 Python
Matlab求解数组中的最大值及它所在的具体位置
Apr 16 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中的多行字符串传递给JavaScript的两种方法
2014/06/19 PHP
服务器迁移php版本不同可能诱发的问题
2015/12/22 PHP
PHP基本语法实例总结
2016/09/09 PHP
php的4种常用运行方式详解
2016/12/22 PHP
Yii框架弹出窗口组件CJuiDialog用法分析
2017/01/07 PHP
简单谈谈 php 文件锁
2017/02/19 PHP
php获取字符串前几位的实例(substr返回字符串的子串用法)
2017/03/08 PHP
永不消失的title提示代码
2007/02/15 Javascript
使用TextRange获取输入框中光标的位置的代码
2007/03/08 Javascript
几个有趣的Javascript Hack
2010/07/24 Javascript
原创javascript小游戏实现代码
2010/08/19 Javascript
JavaScript对象创建及继承原理实例解剖
2013/02/28 Javascript
css3元素简单的闪烁效果实现(html5 jquery)
2013/12/28 Javascript
js阻止默认事件与js阻止事件冒泡示例分享 js阻止冒泡事件
2014/01/27 Javascript
使用原生js封装webapp滑动效果(惯性滑动、滑动回弹)
2014/05/06 Javascript
Javascript 两种刷新方法以及区别和适用范围
2017/01/17 Javascript
使用原生的javascript来实现轮播图
2017/02/24 Javascript
深入理解Vue keep-alive及实践总结
2019/08/21 Javascript
微信小程序事件 bindtap bindinput代码实例
2019/08/26 Javascript
Vue-cli项目部署到Nginx服务器的方法
2019/11/01 Javascript
JS实现星星海特效
2019/12/24 Javascript
jquery选择器和属性对象的操作实例分析
2020/01/10 jQuery
vue中如何添加百度统计代码
2020/12/19 Vue.js
python网络编程实例简析
2014/09/26 Python
Python设计模式之模板方法模式实例详解
2019/01/17 Python
Python实现投影法分割图像示例(二)
2020/01/17 Python
Python如何使用paramiko模块连接linux
2020/03/18 Python
python opencv实现图片缺陷检测(讲解直方图以及相关系数对比法)
2020/04/07 Python
如何解决安装python3.6.1失败
2020/07/01 Python
简单了解Python字典copy与赋值的区别
2020/09/16 Python
加拿大约会网站:EliteSingles.ca
2018/01/12 全球购物
毕业生护理专业个人求职信范文
2014/01/04 职场文书
群众路线教育实践活动学习笔记
2014/11/05 职场文书
预防职务犯罪警示教育心得体会
2016/01/15 职场文书
MySQL 常见存储引擎的优劣
2021/06/02 MySQL
Java移除无效括号的方法实现
2021/08/07 Java/Android