70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
"""后台任务循环启动器:心跳扫描 / 任务调度 / 任务超时,带分布式锁防多实例重复执行。"""
|
|
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() |