APScheduler不完全踩坑指南

这是我参与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
2
3
4
python复制代码# 连接回收时间为1500秒
job_store = {
'default': SQLAlchemyJobStore(url="数据库jdbc连接地址", engine_options={"pool_recycle": 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
2
python复制代码job_store = {
'default': SQLAlchemyJobStore(url="数据库连接地址", pickle_protocol=3, engine_options={"pool_recycle": 1500})

问题3 用gunicorn或者uvicorn重复执行的问题

可以看一下我上一篇文章,用socket/分布式锁都可以解决。

测试平台系列(82) 解决APScheduler重复执行的问题

问题4 分布式部署的问题

如上所说,不管是分布式部署,还是多worker模式。APScheduler的支持都不是很友好。

因为它旨在给单个Python项目完成定时任务方案,而没有考虑集群等分布式问题。在单个python服务里面,他可以运行良好。我们来看一个例子:

  • 服务器A 部署了web服务
  • 服务器B 部署了web服务

2个服务一模一样,之所以部署2台机器,是为了负载均衡考虑。那么我此时调用新增job的方法,请求打到了服务器A,那会是什么过程呢:

  1. 服务器A接收数据,与本地的func联动并加载job到内存
  2. 讲加载后的内存持久化到数据库

但你要说服务器B的jobstore有这个任务吗?那当然是没有的,因为它没有任何集群的概念。不存在服务器A接受命令,送达到服务器B的机制。

这最直观地导致,你在gunicorn/uvicorn开启N个worker,并调用add接口添加job,不会感知到job重复执行的问题。

而一旦你重启了服务,8个worker都从数据库加载了job到内存中,那你会很快遇到重复执行的任务

本文仅限于我对APScheduler的浅薄理解而写,如有不周之处还望指正。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%