基于多进程中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抓取京东图书评论数据
Aug 31 Python
python遍历目录的方法小结
Apr 28 Python
实例讲解Python编程中@property装饰器的用法
Jun 20 Python
python实现求特征选择的信息增益
Dec 18 Python
python统计中文字符数量的两种方法
Jan 31 Python
Python参数解析模块sys、getopt、argparse使用与对比分析
Apr 02 Python
简单了解python 生成器 列表推导式 生成器表达式
Aug 22 Python
Python 创建TCP服务器的方法
Jul 28 Python
Python中实现一行拆多行和多行并一行的示例代码
Sep 06 Python
matplotlib绘制多子图共享鼠标光标的方法示例
Jan 08 Python
python pyhs2 的安装操作
Apr 07 Python
用Python实现Newton插值法
Apr 17 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制作静态网站的模板框架(一)
2006/10/09 PHP
破解图片防盗链的代码(asp/php)测试通过
2010/07/02 PHP
PHP中集成PayPal标准支付的实现方法分享
2012/02/06 PHP
php实现文件下载更能介绍
2012/11/23 PHP
php启动时候提示PHP startup的解决方法
2013/05/07 PHP
php伪静态之APACHE篇
2014/06/02 PHP
Zend Framework基于Command命令行建立ZF项目的方法
2017/02/18 PHP
Yii2.0多文件上传实例说明
2017/07/24 PHP
JS鼠标事件大全 推荐收藏
2011/11/01 Javascript
两种方法实现文本框输入内容提示消失
2013/03/17 Javascript
JavaScript字符串对象toLowerCase方法入门实例(用于把字母转换为小写)
2014/10/17 Javascript
JavaScript中Number.MAX_VALUE属性的使用方法
2015/06/04 Javascript
jQuery 3 中的新增功能汇总介绍
2016/06/12 Javascript
JavaScript事件详细讲解
2016/06/27 Javascript
JavaScript实现url参数转成json形式
2016/09/25 Javascript
BootstrapTable请求数据时设置超时(timeout)的方法
2017/01/22 Javascript
AngularJS实现路由实例
2017/02/12 Javascript
js实现textarea限制输入字数
2017/02/13 Javascript
node文件上传功能简易实现代码
2017/06/16 Javascript
详解React-Native全球化多语言切换工具库react-native-i18n
2017/11/03 Javascript
在vue项目中引入highcharts图表的方法(详解)
2018/03/05 Javascript
如何利用ES6进行Promise封装总结
2019/02/11 Javascript
M2实现Nodejs项目自动部署的方法步骤
2019/05/05 NodeJs
Python MySQLdb 使用utf-8 编码插入中文数据问题
2018/03/13 Python
浅谈Pytorch中的自动求导函数backward()所需参数的含义
2020/02/29 Python
Python更改pip镜像源的方法示例
2020/12/01 Python
Python入门基础之数字字符串与列表
2021/02/01 Python
css3实现冲击波效果的示例代码
2018/01/11 HTML / CSS
家具厂厂长岗位职责
2014/01/01 职场文书
手术室护士长竞聘书
2014/03/31 职场文书
工程承包协议书
2014/04/22 职场文书
计划生育标语
2014/06/23 职场文书
党员检讨书范文
2014/12/27 职场文书
一文带你探究MySQL中的NULL
2021/11/11 MySQL
Vue3中的Refs和Ref详情
2021/11/11 Vue.js
Java中API的使用方法详情
2022/04/06 Java/Android