基于多进程中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之从格式化表达式到方法
Sep 28 Python
快速实现基于Python的微信聊天机器人示例代码
Mar 03 Python
Python寻找两个有序数组的中位数实例详解
Dec 05 Python
python 实现多线程下载视频的代码
Nov 15 Python
详解python opencv、scikit-image和PIL图像处理库比较
Dec 26 Python
opencv之为图像添加边界的方法示例
Dec 26 Python
Python3 操作 MySQL 插入一条数据并返回主键 id的实例
Mar 02 Python
win10安装python3.6的常见问题
Jul 01 Python
Python 中如何使用 virtualenv 管理虚拟环境
Jan 21 Python
Python实现微信表情包炸群功能
Jan 28 Python
python基础之匿名函数详解
Apr 21 Python
利用 Python 的 Pandas和 NumPy 库来清理数据
Apr 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 网页播放器用来播放在线视频的代码(自动判断并选择视频文件类型)
2010/06/03 PHP
探讨如何使用SimpleXML函数来加载和解析XML文档
2013/06/07 PHP
php中字符串和整数比较的操作方法
2019/06/06 PHP
css图片自适应大小
2007/11/28 Javascript
jQuery Dialog 弹出层对话框插件
2010/08/09 Javascript
jQuery操作input type=radio的实现代码
2012/06/14 Javascript
jQuery获取URL请求参数的方法
2015/07/18 Javascript
jQuery实现自定义右键菜单的树状菜单效果
2015/09/02 Javascript
javascript每日必学之继承
2016/02/23 Javascript
详解Angular2中的编程对象Observable
2016/09/17 Javascript
JQuery查找子元素find()和遍历集合each的方法总结
2017/03/07 Javascript
JS实现闭包中的沙箱模式示例
2017/09/07 Javascript
JavaScript使用indexOf()实现数组去重的方法分析
2018/09/04 Javascript
NodeJS加密解密及node-rsa加密解密用法详解
2018/10/12 NodeJs
详解Vue-cli3.X使用px2rem遇到的问题
2019/08/09 Javascript
es6中reduce的基本使用方法
2019/09/10 Javascript
JS实现点星星消除小游戏
2020/03/24 Javascript
js函数柯里化的方法和作用实例分析
2020/04/11 Javascript
python数据结构之二叉树的统计与转换实例
2014/04/29 Python
基于Python中求和函数sum的用法详解
2018/06/28 Python
对pandas数据判断是否为NaN值的方法详解
2018/11/06 Python
为什么Python中没有"a++"这种写法
2018/11/27 Python
python requests 库请求带有文件参数的接口实例
2019/01/03 Python
python开启debug模式的方法
2019/06/27 Python
python:HDF和CSV存储优劣对比分析
2020/06/08 Python
python中怎么表示空值
2020/06/19 Python
python实现自动清理重复文件
2020/08/24 Python
全球采购的街头服饰和帽子:Urban Excess
2020/10/28 全球购物
ShellScript面试题一则-ShellScript编程
2014/06/24 面试题
电子信息工程专业推荐信
2014/02/14 职场文书
实现中国梦思想汇报2014
2014/09/13 职场文书
刑事代理授权委托书
2014/09/17 职场文书
2015年小学语文教学工作总结
2015/05/25 职场文书
2015入党个人自传范文
2015/06/26 职场文书
2016年企业先进员工事迹材料
2016/02/25 职场文书
阿里云国际版 使用Nginx作为HTTPS转发代理服务器
2022/05/11 Servers