FastApi使用装饰器实现定时任务重复执行的问题
装饰器实现定时任务的方法可查看:FastApi定时任务发送钉钉消息
发现部署到服务器运行时同一时间会执行多次
通过日志可以看到同一时间执行了5次
这是因为使用gunicorn启动服务时设置的 workers 进程数是5个
from multiprocessing import cpu_count reload_engine = 'inotify' # //绑定与Nginx通信的端 bind = '0.0.0.0:8000' daemon = True # 守护进程 workers = cpu_count() * 2 + 1 # 进程数 worker_class = 'gevent' # 默认为阻塞模式,最好选择gevent模式,默认的是sync模式 # 维持TCP链接 keepalive = 6 timeout = 65 graceful_timeout = 10 worker_connections = 65535 # 日志级别 loglevel = 'info' # 访问日志路径 accesslog = '/www/wwwlogs/gunicorn_access.log' # 错误日志路径 errorlog = '/www/wwwlogs/gunicorn_error.log' # 设置gunicorn访问日志格式,错误日志无法设置 access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
解决办法
#!/usr/bin/env python # _*_ coding: utf-8 _*_ # 创 建 人: 李先生 # 文 件 名: tasks.py # 创建时间: 2022/9/29 0029 20:32 # @Version:V 0.1 # @desc : import asyncio from functools import wraps from asyncio import ensure_future from starlette.concurrency import run_in_threadpool from typing import Any, Callable, Coroutine, Optional, Union from aioredis import Redis, create_redis_pool from public.log import logger NoArgsNoReturnFuncT = Callable[[], None] NoArgsNoReturnAsyncFuncT = Callable[[], Coroutine[Any, Any, None]] NoArgsNoReturnDecorator = Callable[ [Union[NoArgsNoReturnFuncT, NoArgsNoReturnAsyncFuncT]], NoArgsNoReturnAsyncFuncT ] redis = None # 这是redis全局变量 def repeat_task( *, seconds: float, wait_first: bool = False, raise_exceptions: bool = False, max_repetitions: Optional[int] = None, ) -> NoArgsNoReturnDecorator: def decorator(func: Union[NoArgsNoReturnAsyncFuncT, NoArgsNoReturnFuncT]) -> NoArgsNoReturnAsyncFuncT: is_coroutine = asyncio.iscoroutinefunction(func) @wraps(func) async def wrapped() -> None: repetitions = 0 # 限制定时任务执行次数 global redis if not redis: # redis为None就重新连接 redis = await create_redis_pool(f"redis://:@127.0.0.1:6379/0", password="123456", encoding="utf-8") async def loop() -> None: nonlocal repetitions if wait_first: await asyncio.sleep(seconds) while max_repetitions is None or repetitions < max_repetitions: try: lock = await redis.get(key="LOCK") # 查看是否有lock标记 if not lock: # 没有就执行定时任务 await redis.setex(key="LOCK", value="lock", seconds=seconds) # 任务开始前先设置lock标记 if is_coroutine: # 以协程方式执行 await func() # type: ignore else: # 以线程方式执行 await run_in_threadpool(func) repetitions += 1 else: logger.info(f"多个进程同一时间多次执行定时任务的限制==>{lock}") # 未执行定时任务log打印 except Exception as exc: logger.error(f'执行重复任务异常: {exc}') if raise_exceptions: raise exc await asyncio.sleep(seconds) ensure_future(loop()) return wrapped return decorator
然后再次执行定时任务
看样子是解决了~
愿你走出半生,归来仍是少年!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)