这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战
踩坑指南
这是一篇排错指南,源自博主近期用到APScheduler
较多,特此总结了一些常见的问题,希望对大家有帮助。
问题1: Can’t Connect to MySQL的问题
如果用的是SQLAlchemy作为job的存储介质
,那如果参数没配置好的话,就会导致这个问题。
- 问题描述
在涉及到job数据读取/更新的时候,我们会调用SQLAlchemy
的Session去完成相关操作。但偶尔会弹出Can't Connect to MySQL
或者Lose Connection的错误
。
- 问题分析
MySQL对每个数据库连接是有个有效期的,默认应该是8小时
,但这个是可以配置的。而SQLAlchemy的连接都是在连接池里面,而且有一个默认的回收时间。
假如SQLAlchemy开辟的连接回收时间是2小时,但是MySQL配置的连接失效时间是1小时,那么就会出现一个问题:
SQLAlchemy认为连接仍然有效,但MySQL认为没效了,所以连接到了MySQL那边就不认了
。就会出现以上错误了。
- 解决方法
在jobstore处配置pool_recycle参数,这个参数要确保小于数据库的连接超时时间。
比如我配置25分钟,那么数据库超时时间就算只有30分钟,那我也能正常工作。
1 | python复制代码# 连接回收时间为1500秒 |
问题2 job莫名其妙被删除的问题
不得不说,这个设定很变态
,问题表象是: 你的服务跑着跑着,你的定时任务全不见了
。这个非常非常蛋疼。
- 问题描述
我这边的情况是,当job无法从数据库中反序列化到内存的时候,APScheduler会自动删除这样的job。
- 问题分析
注意看具体的日志,经过我的排查,发现APScheduler用的序列化方式是pickle
,与JSON相比,是个咱们不太熟悉但又听过的序列化类库。
但这个类库有个很严重的问题,比如我的用的python3.7,里面有个pickle.DEFAULT_PROTOCOL
参数,在3.7的时候是3,而到Python3.9以后,这个值变成了4.
APScheduler里面这个参数默认用的pickle.DEFAULT_PROTOCOL参数。
想象一下,你本地是Python3.9, 但服务器是3.7,那你添加job的时候可能是用的DEFAULT_PROTOCOL=4,但是服务器反序列化job的时候,拿到的就是3了,导致无法反序列化,最终删除job。
- 解决方法
指定序列化类型,最好知道你即将部署的机器的python版本,目前可以指定为2或者3。
1 | python复制代码job_store = { |
问题3 用gunicorn或者uvicorn重复执行的问题
可以看一下我上一篇文章,用socket/分布式锁都可以解决。
测试平台系列(82) 解决APScheduler重复执行的问题
问题4 分布式部署的问题
如上所说,不管是分布式部署
,还是多worker模式。APScheduler的支持都不是很友好。
因为它旨在给单个Python项目完成定时任务方案,而没有考虑集群等分布式问题。在单个python服务里面,他可以运行良好。我们来看一个例子:
- 服务器A 部署了web服务
- 服务器B 部署了web服务
2个服务一模一样,之所以部署2台机器,是为了负载均衡
考虑。那么我此时调用新增job的方法,请求打到了服务器A,那会是什么过程呢:
- 服务器A接收数据,与本地的func联动并加载job到内存
- 讲加载后的内存持久化到数据库
但你要说服务器B的jobstore有这个任务吗?那当然是没有的
,因为它没有任何集群的概念。不存在服务器A接受命令,送达到服务器B的机制。
这最直观地导致,你在gunicorn/uvicorn开启N个worker,并调用add接口添加job,不会感知到job重复执行的问题。
而一旦你重启了服务,8个worker都从数据库加载了job到内存中,那你会很快遇到重复执行的任务
。
本文仅限于我对APScheduler的浅薄理解而写,如有不周之处还望指正。
本文转载自: 掘金