基于多进程中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 21 Python
Python引用(import)文件夹下的py文件的方法
Aug 26 Python
Python实现将SQLite中的数据直接输出为CVS的方法示例
Jul 13 Python
对python的bytes类型数据split分割切片方法
Dec 04 Python
python用post访问restful服务接口的方法
Dec 07 Python
快速解决vue.js 模板和jinja 模板冲突的问题
Jul 26 Python
pytorch 获取层权重,对特定层注入hook, 提取中间层输出的方法
Aug 17 Python
使用python 将图片复制到系统剪贴中
Dec 13 Python
python支持多线程的爬虫实例
Dec 21 Python
如何在django中添加日志功能
Feb 06 Python
浅谈tensorflow使用张量时的一些注意点tf.concat,tf.reshape,tf.stack
Jun 23 Python
Python装饰器的练习题
Nov 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
收音机发烧友应当熟知的100条知识
2021/03/02 无线电
php json与xml序列化/反序列化
2013/10/28 PHP
用PHP和Shell写Hadoop的MapReduce程序
2014/04/15 PHP
经典PHP加密解密函数Authcode()修复版代码
2015/04/05 PHP
举例讲解PHP面对对象编程的多态
2015/08/12 PHP
PHP实现上传多图即时显示与即时删除的方法
2017/05/09 PHP
ThinkPHP5&5.1框架关联模型分页操作示例
2019/08/03 PHP
基于jquery的给文章加入关键字链接
2010/10/26 Javascript
实现非常简单的js双向数据绑定
2015/11/06 Javascript
js select下拉联动 更具级联性!
2020/04/17 Javascript
vue.js 1.x与2.0中js实时监听input值的变化
2017/03/15 Javascript
JavaScript获取URL参数的方法之一
2017/03/24 Javascript
通过js修改input、select默认字体颜色
2017/04/19 Javascript
微信小程序如何获取用户手机号
2018/01/26 Javascript
jQuery简单判断值是否存在于数组中的方法示例
2018/04/17 jQuery
微信小程序实现文字无限轮播效果
2018/12/28 Javascript
vue组件间通信六种方式(总结篇)
2019/05/15 Javascript
vue3.0 搭建项目总结(详细步骤)
2019/05/20 Javascript
vuex刷新后数据丢失的解决方法
2020/10/18 Javascript
[04:23]DOTA2上海特锦赛小组赛第一日 TOP10精彩集锦
2016/02/27 DOTA
Python中字典的setdefault()方法教程
2017/02/07 Python
python模拟表单提交登录图书馆
2018/04/27 Python
使用Python的Django和layim实现即时通讯的方法
2018/05/25 Python
Python中函数的基本定义与调用及内置函数详解
2019/05/13 Python
Python 读取串口数据,动态绘图的示例
2019/07/02 Python
Python中注释(多行注释和单行注释)的用法实例
2019/08/28 Python
树莓派4B+opencv4+python 打开摄像头的实现方法
2019/10/18 Python
localStorage的过期时间设置的方法详解
2018/11/26 HTML / CSS
苹果音乐订阅:Apple Music
2018/08/02 全球购物
新入职员工的自我介绍演讲稿
2014/01/02 职场文书
化工操作工岗位职责
2014/04/29 职场文书
升职感谢信
2015/01/22 职场文书
世界遗产的导游词
2015/02/13 职场文书
2015初中生物教研组工作总结
2015/07/21 职场文书
创业方案:赚钱的烧烤店该怎样做?
2019/07/05 职场文书
Zabbix对Kafka topic积压数据监控的解决方案
2022/07/07 Servers