定时任务和fastapi的结合来解决定时任务被关闭的问题
前言:
最早以前使用apscheduler,写了些定时脚本,放在服务器上运行。可服务器是共用的,偶尔会被关闭,导致定时任务不在了,而过了多天才发现。如此反复几次,决定解决这个问题。
方法:
经研究,使用fastapi和apscheduler结合,利用gunicorn作为 WSGI HTTP 服务,这样部署了一个web服务。再配置 gunicorn.service服务开机自启,利用fastapi动态加载的特性,完美的解决了服务器重启定时任务消失的问题,还顺带增加了自动更新定时脚本的功能,一个字:爽。
过程如下:
1.增加tasks文件
此文件主要是写定时任务。导入其他需要执行的类或者函数,在这个里面统一管理任务。内容举例:
1 job_defaults = {'max_instances': 50} # 设置默认的任务实例数量 2 scheduler = BackgroundScheduler(job_defaults=job_defaults) # 实例化调度器 3 4 5 @scheduler.scheduled_job('cron', hour=18, minute=45, second=0) # 每天18点45执行 6 def first_job(): 7 holo_act_time() 8 logging.info("每天18点45的任务执行完毕") 9 10 11 @scheduler.scheduled_job('interval', minutes=30) #每30分钟执行一次 12 def interval_third_job():13 interface_check()
14 logging.info("每30分钟的任务执行完毕")
2.增加fastapi框架,例如命名为start.py
1 from fastapi import FastAPI,Query 2 import logging 3 import uvicorn 4 from contextlib import asynccontextmanager 5 from tasks import scheduler 6 7 8 @asynccontextmanager 9 async def lifespan(app: FastAPI): # 定义一个上下文管理器,用于启动和关闭时执行一些操作 10 scheduler.start() 11 logging.info("定时任务启动") 12 yield 13 scheduler.shutdown() 14 logging.info("定时任务 Shutting down") 15 16 17 app = FastAPI(lifespan=lifespan) # 通过lifespan参数,实现在启动和关闭时执行一些操作 18 19 @app.get("/health/") 20 async def health(): # 健康检查接口 21 return {"status": "success"} 22 23 if __name__ == "__main__": 24 uvicorn.run(app='start:app', host="127.0.0.1", port=8080, reload=True) #app='start:app'中,start与此文件的名称相同
3.增加gunicorn.py
1 import os 2 3 # 设置守护进程 4 daemon=True 5 # 监听内网端口8000 6 bind='0.0.0.0:808sudo 0' 7 # 设置进程文件目录 8 pidfile='./gunicorn.pid' 9 chdir='./' # 工作目录 10 # 工作模式 11 worker_class='uvicorn.workers.UvicornWorker' 12 # 并行工作进程数 核心数*2+1个 13 workers=1 #multiprocessing.cpu_count()+1 14 # 指定每个工作者的线程数 15 threads=4 16 # 设置最大并发量 17 worker_connections = 2000 18 loglevel='debug' # 错误日志的日志级别 19 access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' 20 # 设置访问日志和错误信息日志路径 21 log_dir = "./logs" 22 if not os.path.exists(log_dir): 23 os.makedirs(log_dir) 24 accesslog = "./logs/gunicorn_access.log" 25 errorlog = "./logs/gunicorn_error.log"
在Linux环境下,就可以使用命令启动了:gunicorn start:app -c gunicorn.py
碰到的问题:
1.定时任务的服务被其他用户关闭。
使用另一个定时任务去检查fastapi的一个接口,保障接口是通的,当接口访问失败时发送通知给用户。
这个定时任务建议使用linux的crontab来调度,只要服务器正常,这个任务就能正常执行。
2.定时任务执行失败:
配置了一个定时任务,每天8点执行。结果回头检查时发现执行失败,报错Run time of job ... was missed by ...
经分析,是这个定时任务错误了执行条件。于是在定时任务中增加misfire_grace_time参数,问题解决。
装饰器举例:
@scheduler.scheduled_job('cron', hour=7, minute=10, misfire_grace_time=None)