基于多进程中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函数参数*args**kwargs用法实例
Dec 04 Python
如何搜索查找并解决Django相关的问题
Jun 30 Python
python显示生日是星期几的方法
May 27 Python
Python实现的简单模板引擎功能示例
Sep 02 Python
详解Python3的TFTP文件传输
Jun 26 Python
Python使用itertools模块实现排列组合功能示例
Jul 02 Python
python面向对象实现名片管理系统文件版
Apr 26 Python
对python3 sort sorted 函数的应用详解
Jun 27 Python
python实现按关键字筛选日志文件
Dec 24 Python
Python实现电视里的5毛特效实例代码详解
May 15 Python
django rest framework 过滤时间操作
Jul 12 Python
python中threading和queue库实现多线程编程
Feb 06 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 mail 通过Windows的SMTP发送邮件失败的解决方案
2009/05/27 PHP
php几个预定义变量$_SERVER用法小结
2014/11/07 PHP
PHP中捕获超时事件的方法实例
2015/02/12 PHP
Laravel中Facade的加载过程与原理详解
2017/09/22 PHP
详解Laravel5.6 Passport实现Api接口认证
2018/07/27 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
JavaScript跨域方法汇总
2014/10/16 Javascript
JS实现控制表格单元格垂直对齐的方法
2015/03/30 Javascript
简介JavaScript中getUTCMonth()方法的使用
2015/06/10 Javascript
javascript实现判断鼠标的状态
2015/07/10 Javascript
学习JavaScript设计模式(链式调用)
2015/11/26 Javascript
深入浅析JavaScript中的constructor
2016/04/19 Javascript
vue2.0中click点击当前li实现动态切换class
2017/06/21 Javascript
js实现复制功能(多种方法集合)
2018/01/06 Javascript
JavaScript 有用的代码片段和 trick
2018/02/22 Javascript
angular 用Observable实现异步调用的方法
2018/12/27 Javascript
vue计算属性get和set用法示例
2019/02/08 Javascript
vue如何自动化打包测试环境和正式环境的dist/test文件
2019/06/06 Javascript
layui表格设计以及数据初始化详解
2019/10/26 Javascript
js消除图片小游戏代码
2019/12/11 Javascript
详解三种方式在React中解决绑定this的作用域问题并传参
2020/08/18 Javascript
OpenLayers加载缩放控件使用方法详解
2020/09/25 Javascript
Python实现拼接多张图片的方法
2014/12/01 Python
Python中的多重装饰器
2015/04/11 Python
简化Python的Django框架代码的一些示例
2015/04/20 Python
python清除字符串里非字母字符的方法
2015/07/02 Python
python正则表达式的使用
2017/06/12 Python
python实现搜索文本文件内容脚本
2018/06/22 Python
Sanic框架Cookies操作示例
2018/07/17 Python
HTML5 DeviceOrientation实现手机网站摇一摇功能代码实例
2015/04/24 HTML / CSS
外语系毕业生找工作的求职信
2013/11/28 职场文书
活动总结怎么写啊
2014/05/07 职场文书
大一新生军训新闻稿
2015/07/17 职场文书
2016年优秀团支部事迹材料
2016/02/26 职场文书
Java Socket实现Redis客户端的详细说明
2021/05/26 Redis
OpenCV-Python实现油画效果的实例
2021/06/08 Python