优化
This commit is contained in:
95
arlo.py
95
arlo.py
@ -1,5 +1,16 @@
|
||||
"""Entrypoint for the scheduler process.
|
||||
|
||||
This module starts the APScheduler scheduler and ensures a graceful
|
||||
shutdown on SIGINT/SIGTERM. It also improves job error logging to
|
||||
include exception tracebacks for easier debugging.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import signal
|
||||
import sys
|
||||
import traceback
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
|
||||
from apscheduler.events import EVENT_JOB_ERROR
|
||||
from apscheduler.schedulers.blocking import BlockingScheduler
|
||||
@ -9,28 +20,92 @@ from log.log_manager import log, logger
|
||||
from task.manager_task import manager_task
|
||||
|
||||
|
||||
def job_error_listener(event):
|
||||
if event.exception:
|
||||
logger.error(f"Job {event.job_id} crashed: {str(event.exception)}")
|
||||
# 可添加邮件/钉钉告警逻辑
|
||||
def _format_exception(exc: BaseException) -> str:
|
||||
"""Return a nicely formatted exception with traceback."""
|
||||
return "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def job_error_listener(event: Any) -> None:
|
||||
"""Listener for job errors that logs the exception and traceback.
|
||||
|
||||
Uses the APScheduler event object. This is defensive: if an exception
|
||||
is present it logs its string representation and the full traceback to
|
||||
the configured logger. Keep this lightweight to avoid raising inside
|
||||
the listener.
|
||||
"""
|
||||
try:
|
||||
if getattr(event, "exception", None):
|
||||
exc = event.exception
|
||||
logger.error(f"Job {getattr(event, 'job_id', '<unknown>')} crashed: {exc}")
|
||||
logger.error(_format_exception(exc))
|
||||
# 可添加邮件告警逻辑(如果需要且已配置)
|
||||
except Exception:
|
||||
# We must not let the listener raise — log and continue.
|
||||
logger.exception("Exception in job_error_listener")
|
||||
|
||||
|
||||
def _validate_interval(value: Any) -> int:
|
||||
"""Return a valid positive integer interval in seconds.
|
||||
|
||||
If the provided value is invalid, returns a safe default (300).
|
||||
"""
|
||||
DEFAULT = 300
|
||||
try:
|
||||
iv = int(value)
|
||||
if iv <= 0:
|
||||
raise ValueError("interval must be > 0")
|
||||
return iv
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Invalid config.scheduler_interval=%r, falling back to %s seconds",
|
||||
value,
|
||||
DEFAULT,
|
||||
)
|
||||
return DEFAULT
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Create and start the scheduler with graceful shutdown handling."""
|
||||
scheduler = BlockingScheduler()
|
||||
# 每隔config.scheduler_interval秒执行一次任务,同时设定第一次执行在程序启动后10秒后执行
|
||||
|
||||
interval_seconds = _validate_interval(getattr(config, "scheduler_interval", None))
|
||||
|
||||
# 每隔 interval_seconds 秒执行一次任务,同时设定第一次执行在程序启动10秒后执行
|
||||
scheduler.add_job(
|
||||
partial(manager_task, scheduler),
|
||||
'interval',
|
||||
seconds=config.scheduler_interval,
|
||||
jitter=30, # 添加随机抖动避免任务雪崩
|
||||
next_run_time=datetime.datetime.now() + datetime.timedelta(seconds=10) # 替代 date 触发器
|
||||
"interval",
|
||||
seconds=interval_seconds,
|
||||
jitter=30,
|
||||
next_run_time=datetime.datetime.now() + datetime.timedelta(seconds=10),
|
||||
)
|
||||
|
||||
# 添加任务错误监听器
|
||||
scheduler.add_listener(job_error_listener, EVENT_JOB_ERROR)
|
||||
|
||||
# Graceful shutdown handlers
|
||||
def _shutdown(signum, frame):
|
||||
log(f"Received signal {signum}. Shutting down scheduler...")
|
||||
try:
|
||||
scheduler.shutdown(wait=False)
|
||||
except Exception:
|
||||
logger.exception("Error while shutting down scheduler")
|
||||
# Ensure process exits after scheduler shutdown
|
||||
# sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, _shutdown)
|
||||
signal.signal(signal.SIGTERM, _shutdown)
|
||||
|
||||
try:
|
||||
log("started successfully.")
|
||||
scheduler.start() # 阻塞运行
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
log("Shutting down ...")
|
||||
if scheduler.state == 1:
|
||||
try:
|
||||
scheduler.shutdown(wait=False)
|
||||
except Exception:
|
||||
logger.exception("Error while shutting down scheduler")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user