基于多进程中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 实现插入排序算法
Jun 05 Python
Python中正则表达式的用法实例汇总
Aug 18 Python
Python的包管理器pip更换软件源的方法详解
Jun 20 Python
windows及linux环境下永久修改pip镜像源的方法
Nov 28 Python
简单谈谈Python中的元祖(Tuple)和字典(Dict)
Apr 21 Python
python通过opencv实现批量剪切图片
Nov 13 Python
python简单实现操作Mysql数据库
Jan 29 Python
使用11行Python代码盗取了室友的U盘内容
Oct 23 Python
Django用户认证系统 组与权限解析
Aug 02 Python
pytorch实现focal loss的两种方式小结
Jan 02 Python
Python 实现网课实时监控自动签到、打卡功能
Mar 12 Python
Python3 pickle对象串行化代码实例解析
Mar 23 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
收音机鉴频器对声音的影响和频偏分析
2021/03/02 无线电
php模板中出现空行解决方法
2011/03/08 PHP
PHP实现取得HTTP请求的原文
2014/08/18 PHP
OfflineSave离线保存代码再次发布使用说明
2007/05/23 Javascript
javascript this用法小结
2008/12/19 Javascript
js控制CSS样式属性语法对照表
2012/12/11 Javascript
JS实现随机化快速排序的实例代码
2013/08/01 Javascript
jquery实现仿Flash的横向滑动菜单效果代码
2015/09/17 Javascript
js 调用百度分享功能
2017/02/27 Javascript
jQuery中animate()的使用方法及解决$(”body“).animate({“scrollTop”:top})不被Firefox支持的问题
2017/04/04 jQuery
使用node.js搭建服务器
2017/05/20 Javascript
vue 运用mock数据的示例代码
2017/11/07 Javascript
JS/HTML5游戏常用算法之追踪算法实例详解
2018/12/12 Javascript
Nodejs对postgresql基本操作的封装方法
2019/02/20 NodeJs
vue封装swiper代码实例解析
2019/10/08 Javascript
python实现迭代法求方程组的根过程解析
2019/11/25 Javascript
Javascript异步编程async实现过程详解
2020/04/02 Javascript
[01:06:30]DOTA2-DPC中国联赛定级赛 Phoenix vs DLG BO3第二场 1月9日
2021/03/11 DOTA
Python translator使用实例
2008/09/06 Python
python实现的希尔排序算法实例
2015/07/01 Python
python爬取51job中hr的邮箱
2016/05/14 Python
Python简单删除列表中相同元素的方法示例
2017/06/12 Python
利用python求相邻数的方法示例
2017/08/18 Python
简单了解Django模板的使用
2017/12/20 Python
python 重命名轴索引的方法
2018/11/10 Python
Python调用钉钉自定义机器人的实现
2020/01/03 Python
Keras 切换后端方式(Theano和TensorFlow)
2020/06/19 Python
THE OUTNET美国官网:国际设计师品牌折扣网站
2017/03/07 全球购物
旅游管理专业学生求职信
2013/09/28 职场文书
自荐信的两点禁忌
2013/10/30 职场文书
银行开业庆典方案
2014/02/06 职场文书
领导调研接待方案
2014/02/27 职场文书
乡镇群众路线整改落实情况汇报
2014/10/28 职场文书
优秀校长事迹材料
2014/12/24 职场文书
拾金不昧表扬稿
2015/01/16 职场文书
详解nginx.conf 中 root 目录设置问题
2021/04/01 Servers