"""后台任务循环启动器:心跳扫描 / 任务调度 / 任务超时,带分布式锁防多实例重复执行。""" import asyncio import logging import redis.asyncio as aioredis from app.config import settings from app.constants import LOCK_HEARTBEAT, LOCK_SCHEDULER, LOCK_TIMEOUT from app.services.heartbeat import HeartbeatService from app.services.lock import Lock from app.services.relay import RelayService from app.services.scheduler import Scheduler from app.services.timeout import TimeoutService logger = logging.getLogger(__name__) class SchedulerLoop: def __init__(self, redis: aioredis.Redis): self.redis = redis self._tasks: list[asyncio.Task] = [] async def start(self) -> None: self._tasks.append(asyncio.create_task(self._heartbeat_loop())) self._tasks.append(asyncio.create_task(self._dispatch_loop())) self._tasks.append(asyncio.create_task(self._timeout_loop())) logger.info("scheduler loops started") async def stop(self) -> None: for t in self._tasks: t.cancel() for t in self._tasks: try: await t except asyncio.CancelledError: pass self._tasks.clear() async def _heartbeat_loop(self) -> None: hb = HeartbeatService(self.redis) while True: await asyncio.sleep(settings.heartbeat_scan_interval) lock = Lock(self.redis, LOCK_HEARTBEAT) if await lock.acquire(): try: await hb.scan() finally: await lock.release() async def _dispatch_loop(self) -> None: sched = Scheduler(self.redis, relay=RelayService(self.redis)) while True: await asyncio.sleep(settings.dispatch_interval) lock = Lock(self.redis, LOCK_SCHEDULER) if await lock.acquire(): try: await sched.dispatch_pending(settings.dispatch_batch_size) finally: await lock.release() async def _timeout_loop(self) -> None: to = TimeoutService(self.redis) while True: await asyncio.sleep(settings.task_timeout_check_interval) lock = Lock(self.redis, LOCK_TIMEOUT) if await lock.acquire(): try: await to.check() finally: await lock.release()