基于多进程中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实现批量下载文件
May 17 Python
代码讲解Python对Windows服务进行监控
Feb 11 Python
使用Python的Django和layim实现即时通讯的方法
May 25 Python
Python 字符串与二进制串的相互转换示例
Jul 23 Python
django框架实现模板中获取request 的各种信息示例
Jul 01 Python
pybind11和numpy进行交互的方法
Jul 04 Python
Django使用 Bootstrap 样式修改书籍列表过程解析
Aug 09 Python
浅析python内置模块collections
Nov 15 Python
python实现处理mysql结果输出方式
Apr 09 Python
Cpython解释器中的GIL全局解释器锁
Nov 09 Python
python wsgiref源码解析
Feb 06 Python
Python中快速掌握Data Frame的常用操作
Mar 31 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使用json_encode对变量json编码
2014/04/07 PHP
Laravel 5.3 学习笔记之 错误&日志
2016/08/28 PHP
PHP数组实际占用内存大小原理解析
2020/12/11 PHP
javascript encodeURI和encodeURIComponent的比较
2010/04/03 Javascript
window.addEventListener来解决让一个js事件执行多个函数
2012/12/26 Javascript
javascript通过class来获取元素实现代码
2013/02/20 Javascript
在jquery中combobox多选的不兼容问题总结
2013/12/24 Javascript
js从Cookies里面取值的简单实现
2014/06/30 Javascript
使用JavaScript为Kindeditor自定义按钮增加Audio标签
2016/03/18 Javascript
PHP获取当前页面完整URL的方法
2016/12/02 Javascript
浅谈 Vue 项目优化的方法
2017/12/16 Javascript
浅谈JavaScript 代码整洁之道
2018/10/23 Javascript
vue实现select下拉显示隐藏功能
2019/09/30 Javascript
详解Angular Karma测试的持续集成实践
2019/11/15 Javascript
vue使用map代替Aarry数组循环遍历的方法
2020/04/30 Javascript
详解webpack的文件监听实现(热更新)
2020/09/11 Javascript
pip 错误unused-command-line-argument-hard-error-in-future解决办法
2014/06/01 Python
python抓取百度首页的方法
2015/05/19 Python
Win10下Python环境搭建与配置教程
2016/11/18 Python
Python正则抓取网易新闻的方法示例
2017/04/21 Python
对python中array.sum(axis=?)的用法介绍
2018/06/28 Python
padas 生成excel 增加sheet表的实例
2018/12/11 Python
Python实现的各种常见分布算法示例
2018/12/13 Python
Python实现栈和队列的简单操作方法示例
2019/11/29 Python
python rsa-oaep加密的示例代码
2020/09/23 Python
python实现录音功能(可随时停止录音)
2020/10/26 Python
python中的插入排序的简单用法
2021/01/19 Python
python 制作网站筛选工具(附源码)
2021/01/21 Python
德国高性价比网上药店:medpex
2017/07/09 全球购物
英国一家集合了众多有才华设计师品牌的奢侈店:Wolf & Badger
2018/04/18 全球购物
如何通过 CSS 写出火焰效果
2021/03/24 HTML / CSS
应届大学生简历中的自我评价
2014/01/15 职场文书
报考公务员诚信承诺书
2014/08/29 职场文书
预备党员表决心的话
2015/09/22 职场文书
读《工匠精神》有感:热爱工作,精益求精
2019/12/28 职场文书
windows11怎么查看自己安装的版本号? win11版本号的查看方法
2021/11/21 数码科技