FastAPI uses decorators to implement timing tasks

Because FastAPI itself is a high-performance asynchronous framework, FastAPI can also easily implement timing tasks without using any third-party timing task modules.

Create a tasks.pyfile, copy the decorator code below:

import asyncio
from loguru import logger
from functools import wraps
from asyncio import ensure_future
from starlette.concurrency import run_in_threadpool
from typing import Any, Callable, Coroutine, Optional, Union

NoArgsNoReturnFuncT = Callable[[], None]
NoArgsNoReturnAsyncFuncT = Callable[[], Coroutine[Any, Any, None]]
NoArgsNoReturnDecorator = Callable[
    [Union[NoArgsNoReturnFuncT, NoArgsNoReturnAsyncFuncT]],
    NoArgsNoReturnAsyncFuncT
]


def repeat_task(
    *,
    seconds: float,
    wait_first: bool = False,
    raise_exceptions: bool = False,
    max_repetitions: Optional[int] = None,
) -> NoArgsNoReturnDecorator:
    '''
    返回一个修饰器, 该修饰器修改函数, 使其在首次调用后定期重复执行.
    其装饰的函数不能接受任何参数并且不返回任何内容.
    参数:
        seconds: float
            等待重复执行的秒数
        wait_first: bool (默认 False)
            如果为 True, 该函数将在第一次调用前先等待一个周期.
        raise_exceptions: bool (默认 False)
            如果为 True, 该函数抛出的错误将被再次抛出到事件循环的异常处理程序.
        max_repetitions: Optional[int] (默认 None)
            该函数重复执行的最大次数, 如果为 None, 则该函数将永远重复.
    '''
    def decorator(func: Union[NoArgsNoReturnAsyncFuncT, NoArgsNoReturnFuncT]) -> NoArgsNoReturnAsyncFuncT:
        '''
        将修饰函数转换为自身重复且定期调用的版本.
        '''
        is_coroutine = asyncio.iscoroutinefunction(func)
        had_run = False

        @wraps(func)
        async def wrapped() -> None:
            nonlocal had_run
            if had_run:
                return
            had_run = True
            repetitions = 0

            async def loop() -> None:
                nonlocal repetitions
                if wait_first:
                    await asyncio.sleep(seconds)
                while max_repetitions is None or repetitions < max_repetitions:
                    try:
                        if is_coroutine:
                            # 以协程方式执行
                            await func()  # type: ignore
                        else:
                            # 以线程方式执行
                            await run_in_threadpool(func)
                        repetitions += 1
                    except Exception as exc:
                        logger.error(f'执行重复任务异常: {
      
      exc}')
                        if raise_exceptions:
                            raise exc
                    await asyncio.sleep(seconds)
            ensure_future(loop())
        return wrapped
    return decorator

When calling in main.py, we need to add @app.on_event('startup')the decorator first, and then add our own implemented repeat_taskdecorator:

......
from core.tasks import repeat_task
......
@app.on_event('startup')
@repeat_task(seconds=60*60, wait_first=True)
def repeat_task_aggregate_request_records() -> None:
    logger.info('触发重复任务: 聚合请求记录')
......

Change the repeat period to 6 seconds to test the effect:

INFO:     Started server process [2075595]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8083 (Press CTRL+C to quit)
2022-05-31 19:31:44.065 | INFO     | apis.bases.api_logs:repeat_task_aggregate_request_records:52 - 触发重复任务: 聚合请求记录
2022-05-31 19:31:50.067 | INFO     | apis.bases.api_logs:repeat_task_aggregate_request_records:52 - 触发重复任务: 聚合请求记录
2022-05-31 19:31:56.068 | INFO     | apis.bases.api_logs:repeat_task_aggregate_request_records:52 - 触发重复任务: 聚合请求记录

The above only implements a repeat_taskdecorator that is executed repeatedly on a regular basis. If you want to achieve more requirements, you only need to follow the gourd painting and implement a few more decorators.

Guess you like

Origin blog.csdn.net/hekaiyou/article/details/125072249