基于多进程中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中的readline()方法的使用
May 24 Python
深入理解Django的中间件middleware
Mar 14 Python
python如何将图片转换为字符图片
Aug 19 Python
python 实现判断ip连通性的方法总结
Apr 22 Python
python读取文本中的坐标方法
Oct 14 Python
在unittest中使用 logging 模块记录测试数据的方法
Nov 30 Python
对Python3之进程池与回调函数的实例详解
Jan 22 Python
解决Pycharm调用Turtle时 窗口一闪而过的问题
Feb 16 Python
numpy库与pandas库axis=0,axis= 1轴的用法详解
May 27 Python
Python缓存技术实现过程详解
Sep 25 Python
python通过matplotlib生成复合饼图
Feb 06 Python
python音频处理的示例详解
Dec 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
为PHP安装imagick时出现Cannot locate header file MagickWand.h错误的解决方法
2014/11/03 PHP
非常实用的PHP常用函数汇总
2014/12/17 PHP
PHP中Array相关函数简介
2016/07/03 PHP
Visual Studio中的jQuery智能提示设置方法
2010/03/27 Javascript
Js基础学习资料
2010/11/23 Javascript
仅Firefox中链接A无法实现模拟点击以触发其默认行为
2011/07/31 Javascript
Javascript Boolean、Nnumber、String 强制类型转换的区别详细介绍
2012/12/13 Javascript
jQuery获得页面元素的绝对/相对位置即绝对X,Y坐标
2014/03/06 Javascript
Javascript核心读书有感之语言核心
2015/02/01 Javascript
jQuery实现提交按钮点击后变成正在处理字样并禁止点击的方法
2015/03/24 Javascript
js结合正则实现国内手机号段校验
2015/06/19 Javascript
纯JavaScript实现的分页插件实例
2015/07/14 Javascript
Node.js配合node-http-proxy解决本地开发ajax跨域问题
2016/08/31 Javascript
input框中的name和id的区别
2016/11/16 Javascript
微信小程序 小程序制作及动画(animation样式)详解
2017/01/06 Javascript
基于jQuery实现照片墙自动播放特效
2017/01/12 Javascript
vue.js动态数据绑定学习笔记
2017/05/19 Javascript
详解使用VueJS开发项目中的兼容问题
2018/08/02 Javascript
JS实现的冒泡排序,快速排序,插入排序算法示例
2019/03/02 Javascript
js实现贪吃蛇游戏(简易版)
2020/09/29 Javascript
JavaScript实现京东快递单号查询
2020/11/30 Javascript
通过滑动翻页效果实现和移动端click事件问题
2021/01/26 Javascript
[00:15]天涯墨客终极技能展示
2018/08/25 DOTA
[56:42]完美世界DOTA2联赛循环赛 Matador vs Forest 第二场 11.06
2020/11/06 DOTA
Python3中使用PyMongo的方法详解
2017/07/28 Python
神经网络python源码分享
2017/12/15 Python
python游戏开发之视频转彩色字符动画
2019/04/26 Python
浅谈keras中的keras.utils.to_categorical用法
2020/07/02 Python
Python Unittest原理及基本使用方法
2020/11/06 Python
Python利用socket模块开发简单的端口扫描工具的实现
2021/01/27 Python
无毒社区工作方案
2014/05/23 职场文书
建设工地安全标语
2014/06/07 职场文书
门卫岗位职责说明书
2014/08/18 职场文书
网上祭先烈心得体会
2014/09/01 职场文书
仰望星空观后感
2015/06/10 职场文书
GTX1660显卡搭配显示器推荐
2022/04/19 数码科技