调度、监控与部署:Python自动化任务全栈实践

一、引言:为什么你需要一个完整的自动化体系?

如果你写过Python脚本,一定遇到过这种情况:写了个爬虫想每天凌晨抓点数据,或者写了个脚本定时清理临时文件,又或者需要每天上班前自动生成一份报表。一开始你可能兴致勃勃,每天手动双击运行一下,但没过几天就烦了------要么忘了,要么觉得太麻烦。

手动执行脚本,本质上是在用人的注意力来驱动自动化,这本身就违背了"自动化"的初衷。当你有3个脚本时,也许还能勉强坚持;当你有10个脚本、20个脚本时,手动执行就变成了不可能完成的任务。

这时候,你需要一套完整的自动化体系。这套体系至少包含三个核心能力:

  • 调度能力:让脚本在指定的时间或条件下自动触发执行;
  • 监控能力:知道脚本在执行过程中发生了什么,是成功还是失败,运行了多久,有没有异常;
  • 部署能力:把写好的脚本打包、分发,方便地部署到目标机器上。

本文将围绕这三个核心能力,从Python自动化任务的调度、监控到部署,给出完整的技术方案和实践指南。

二、任务调度:让脚本自己"醒"来

2.1 方案概览与选型对比

在Python生态中,实现定时任务调度的方案非常丰富。下面用一张表格快速对比主流的几种方式:

方案 适用场景 复杂度 持久化 跨平台 典型用途
time.sleep() 简单脚本、临时测试 极低 脚本内部简单循环
threading.Timer 单次延时执行 延迟任务、重试机制
schedule库 中小型项目、快速原型 日常运维、定时清理
APScheduler 复杂调度、Web应用 支持 后台任务、数据同步
Windows任务计划程序 Windows环境系统级调度 系统级 仅Windows 开机启动、后台任务
cron/Linux Linux环境系统级调度 系统级 仅Unix 系统级定时任务
Celery 大型分布式系统 支持 分布式任务队列

在实际生产环境中,我的建议是:小型项目用schedule库快速上手,中大型项目或需要持久化的场景选择APScheduler,系统级守护任务则配合Windows任务计划程序或systemd使用。

2.2 Python内置方案:最简单的入门方式

2.2.1 time.sleep()------最直接但不推荐用于生产

如果你的需求非常简单,比如在单个脚本内部每隔一段时间执行一次任务,可以使用time.sleep()

python 复制代码
import time

def task():
    print("Task executed")

while True:
    task()
    time.sleep(60)  # 每60秒执行一次

这种方式几乎没有依赖,实现极其简单,但缺点也很明显:不适合长时间运行的服务,一旦程序被中断,任务就会丢失。此外,sleep()期间程序处于阻塞状态,无法响应其他事件。

2.2.2 threading.Timer------单次延时执行的优雅方案

对于需要延时执行一次的任务,可以使用threading.Timer

python 复制代码
from threading import Timer

def job():
    print("Delayed task executed")
    # 如果需要重复执行,可以在job内部再次调用Timer
    Timer(60, job).start()

Timer(60, job).start()

这种方案适合需要在同一线程中执行定时任务的场景,但受限于主线程的生命周期,且实现重复调度需要在函数内部递归调用,不够优雅。

2.2.3 sched模块------标准库中的调度器

Python标准库还提供了sched模块,可以实现更精细的事件调度:

python 复制代码
import sched
import time

s = sched.scheduler(time.time, time.sleep)

def task(name):
    print(f"Task {name} executed at {time.time()}")

s.enter(10, 1, task, argument=('first',))
s.run()

sched模块比单纯的sleep更灵活,支持多任务优先级调度,但同样需要程序持续运行。

2.3 schedule库:轻量级、易上手的纯Python调度方案

如果你需要一个轻量级的调度方案,但又不想被操作系统绑定,schedule库是最佳选择。它的API设计非常直观,语法接近自然语言。

2.3.1 基础用法
python 复制代码
import schedule
import time

def job():
    print("This is a scheduled job.")

# 每隔一段时间执行一次
schedule.every(5).seconds.do(job)
schedule.every(1).minutes.do(job)
schedule.every().hour.do(job)

# 每天的特定时间执行
schedule.every().day.at("10:30").do(job)

# 每周的特定时间执行
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

schedule.every()方法配合.seconds.minutes.hour等属性可以指定调度频率,使用.at()可以指定每天的特定时间点。

2.3.2 进阶技巧:取消任务与传参
python 复制代码
import schedule
import time

def greet(name):
    print(f"Hello, {name}!")

# 返回Job对象
job = schedule.every(2).seconds.do(greet, name="World")

# 运行一段时间后取消任务
time.sleep(10)
schedule.cancel_job(job)

print("Job cancelled")

while True:
    schedule.run_pending()
    time.sleep(1)
2.3.3 schedule库的局限性

schedule库的优势在于简单易用,但它有一个核心局限:它只是一个"调度器",本身不负责"执行"。你需要自己维护一个无限循环来不断检查并执行待办任务,这意味着你的脚本必须一直运行,且不适合分布式场景。此外,schedule不支持任务持久化------程序重启后所有调度信息都会丢失。

2.4 APScheduler:工业级的Python调度框架

如果你的需求超出简单循环任务,比如需要cron表达式、任务持久化、并发控制等高级特性,APScheduler是最佳选择。APScheduler(Advanced Python Scheduler)是Python生态中功能强大的定时任务框架,支持一次性任务、固定间隔任务、Cron表达式任务,并提供作业持久化、并发控制、错误监听等高级特性。

2.4.1 核心组件解析

APScheduler由四个核心组件构成,理解它们之间的协作关系是掌握APScheduler的关键:

组件 作用 常用类型
调度器(Scheduler) 任务调度的核心,协调各组件 BlockingScheduler(阻塞式)、BackgroundScheduler(后台非阻塞式)
触发器(Trigger) 定义任务执行的时间规则 date(一次性)、interval(固定间隔)、cron(Cron表达式)
执行器(Executor) 负责执行任务 ThreadPoolExecutor(线程池)、ProcessPoolExecutor(进程池)
作业存储(JobStore) 存储任务信息 MemoryJobStore(内存)、SQLAlchemyJobStore(数据库)、RedisJobStore(Redis)
2.4.2 安装与基础配置
bash 复制代码
pip install apscheduler

# 如需持久化到数据库,可安装扩展
pip install apscheduler[sqlalchemy]   # SQLite/MySQL/PostgreSQL
pip install apscheduler[redis]        # Redis存储
pip install apscheduler[mongodb]      # MongoDB存储
2.4.3 三种触发器详解

(1)date触发器------一次性任务

适用于需要在特定时间点执行一次的作业:

python 复制代码
from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

def one_time_job():
    print(f"Job executed at {datetime.now()}")

scheduler = BlockingScheduler()
scheduler.add_job(one_time_job, 'date', run_date=datetime(2026, 5, 1, 10, 30, 0))

scheduler.start()

(2)interval触发器------固定间隔任务

适用于周期性重复执行的作业,如每5分钟检查一次系统状态:

python 复制代码
from apscheduler.schedulers.background import BackgroundScheduler

def recurring_job():
    print("Recurring job executed")

scheduler = BackgroundScheduler()
scheduler.add_job(recurring_job, 'interval', minutes=5, start_date='2026-05-01 00:00:00', end_date='2026-12-31 23:59:59')
scheduler.start()

interval触发器支持秒、分、时、天等多种时间单位,还可以通过start_dateend_date控制作业的生命周期。

(3)cron触发器------最灵活的调度方式

cron触发器是APScheduler中最强大也最常用的触发器,它使用与Linux crontab类似的表达式来定义复杂的调度规则:

python 复制代码
from apscheduler.schedulers.background import BackgroundScheduler

def cron_job():
    print("Cron job executed")

scheduler = BackgroundScheduler()

# 每天早上9点执行
scheduler.add_job(cron_job, 'cron', hour=9, minute=0)

# 每周一到周五的9点到17点之间,每隔5分钟执行一次
scheduler.add_job(cron_job, 'cron', day_of_week='mon-fri', hour='9-17', minute='*/5')

# 使用标准crontab字符串
scheduler.add_job(cron_job, CronTrigger.from_crontab('0 2 * * *'))  # 每天凌晨2点

scheduler.start()

cron表达式中的各个字段对应关系为:minute、hour、day of month、month、day of week。

2.4.4 调度器类型的选择

APScheduler提供了多种调度器以适应不同的应用场景:

  • BlockingScheduler :调用start()后会阻塞当前线程,适用于独立运行的脚本;
  • BackgroundScheduler:在后台非阻塞地运行,适用于Web应用(如Django、Flask);
  • AsyncIOScheduler:适配asyncio异步框架;
  • GeventScheduler / QtScheduler:对应框架的专属调度器。
2.4.5 任务持久化:让调度信息永不丢失

生产环境中,任务的持久化至关重要。APScheduler支持将任务存储到数据库中,即使调度器重启也不会丢失任务信息:

python 复制代码
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

# 配置执行器和作业存储
jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': ThreadPoolExecutor(max_workers=20),
    'processpool': ProcessPoolExecutor(max_workers=5)
}
scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, timezone='Asia/Shanghai')

def my_job():
    print("Persistent job executed")

scheduler.add_job(my_job, 'interval', minutes=10, id='my_unique_job')
scheduler.start()

⚠️ 重要提醒 :务必设置时区(如timezone='Asia/Shanghai'),否则在多时区环境下调度可能出现偏差。

2.4.6 APScheduler实战场景

场景一:FastAPI中集成定时任务

在FastAPI应用中,APScheduler可以方便地实现后台定时任务:

python 复制代码
from fastapi import FastAPI
from apscheduler.schedulers.background import BackgroundScheduler
from contextlib import asynccontextmanager

scheduler = BackgroundScheduler()

def background_task():
    print("Running background task...")

@asynccontextmanager
async def lifespan(app: FastAPI):
    scheduler.add_job(background_task, 'interval', minutes=5)
    scheduler.start()
    yield
    scheduler.shutdown()

app = FastAPI(lifespan=lifespan)

场景二:定时数据同步与巡检

在自动化运维场景中,APScheduler可用于定时采集数据、执行巡检任务:

python 复制代码
from apscheduler.schedulers.background import BackgroundScheduler
import psutil

def system_check():
    cpu = psutil.cpu_percent(interval=1)
    mem = psutil.virtual_memory().percent
    if cpu > 80 or mem > 85:
        print(f"ALERT: CPU={cpu}%, MEM={mem}%")
        # 发送告警...

scheduler = BackgroundScheduler()
scheduler.add_job(system_check, 'interval', minutes=1)
scheduler.start()

2.5 Windows任务计划程序:系统级的稳定调度

对于需要在Windows环境下长期稳定运行的脚本,Windows系统自带的"任务计划程序"是最可靠的选择。它本质上是一个系统级的定时器,能够按照设定的时间、频率,或者响应系统事件(如开机、登录)来启动任何程序。

2.5.1 打开任务计划程序

按下Win + R组合键,输入taskschd.msc后回车,即可快速打开任务计划程序主界面。

2.5.2 创建基本任务(向导模式)

对于新手,推荐使用"创建基本任务"向导模式:

  1. 命名和描述:建议认真填写"名称"和"描述"。例如,名称可以叫"每日销售报表生成",描述里写上"每天上午9点运行,调用daily_report.py"。好的描述能让你(或你的同事)在维护时快速理解任务用途。

  2. 选择触发器:可选每日、每周、每月、一次性、计算机启动时、用户登录时等。

  3. 设置操作 :选择"启动程序",在"程序或脚本"中填写Python解释器的完整路径(如C:\Python39\python.exe),在"添加参数"中填写脚本的完整路径(如D:\scripts\daily_report.py),在"起始于"中填写脚本所在目录。

  4. 完成设置:检查所有配置后点击"完成"。

2.5.3 高级配置与最佳实践

(1)绕过用户登录------真正开机自启

如果希望脚本在系统启动后立即运行(例如服务器场景),需要在任务属性的"常规"选项卡中勾选"不管用户是否登录都要运行",并在"触发器"中选择"在系统启动时"。注意,系统会提示"不存储密码时只在用户登录时运行"------这实际上是要求你输入密码并勾选相关选项,否则会配置失败。

(2)使用批处理文件封装

由于任务计划程序直接调用Python脚本可能存在路径解析问题,建议使用批处理文件作为中间层:

batch 复制代码
@echo off
C:\Python39\python.exe D:\scripts\daily_report.py
exit /b 0

(3)隐藏运行窗口

如果不想在运行时弹出命令行窗口,可以使用pythonw.exe代替python.exe,并在任务设置中勾选"隐藏"。

(4)日志捕获与异常处理

务必在脚本中添加日志捕获机制,避免静默崩溃:

python 复制代码
import logging
import traceback

logging.basicConfig(filename='task.log', level=logging.INFO)

try:
    # 主任务逻辑
    pass
except Exception as e:
    logging.error(f"Task failed: {e}")
    logging.error(traceback.format_exc())

三、监控:让自动化"可见"

调度解决了"何时执行"的问题,但执行过程发生了什么、执行结果如何,同样至关重要。没有监控的自动化就像没有仪表的驾驶------你永远不知道车况如何。

3.1 日志管理:监控的基础设施

Python内置的logging模块是日志管理的核心工具,配合合理的配置可以实现生产级别的日志体系。

3.1.1 日志分级与配置

合理的日志分级是监控的第一步:

python 复制代码
import logging
import logging.handlers
import os

# 配置日志格式
log_format = '%(asctime)s - %(levelname)s - [%(threadName)s] - %(name)s - %(message)s'
log_datefmt = '%Y-%m-%d %H:%M:%S'

# 获取根日志器
logger = logging.getLogger('task_monitor')
logger.setLevel(logging.DEBUG)

# 控制台输出
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter(log_format, datefmt=log_datefmt))
logger.addHandler(console_handler)

# 文件轮转输出(按天轮转,保留30天)
file_handler = logging.handlers.TimedRotatingFileHandler(
    'logs/task.log',
    when='midnight',
    interval=1,
    backupCount=30,
    encoding='utf-8'
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(log_format, datefmt=log_datefmt))
logger.addHandler(file_handler)

使用TimedRotatingFileHandler可以实现按小时、日、周的自动日志轮转,配合backupCount控制保留周期。

3.1.2 日志分级策略

日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)的合理使用是日志管理的第一步。建议采用"分级+标签"的复合策略:

  • DEBUG:开发调试信息,生产环境关闭
  • INFO:正常任务执行的关键节点
  • WARNING:预期内的问题,不影响任务结果
  • ERROR:任务执行失败但可恢复
  • CRITICAL:严重故障,需要立即人工介入

3.2 任务执行监控

3.2.1 记录任务执行上下文

每个任务执行时,应当记录足够的信息用于事后分析:

python 复制代码
import time
import uuid
from functools import wraps

def monitor_task(task_name):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            task_id = str(uuid.uuid4())[:8]
            start_time = time.time()
            logger.info(f"[{task_name}] Task {task_id} started")
            
            try:
                result = func(*args, **kwargs)
                elapsed = time.time() - start_time
                logger.info(f"[{task_name}] Task {task_id} completed in {elapsed:.2f}s")
                return result
            except Exception as e:
                elapsed = time.time() - start_time
                logger.error(f"[{task_name}] Task {task_id} failed after {elapsed:.2f}s: {str(e)}")
                raise
        return wrapper
    return decorator

@monitor_task("daily_report")
def generate_report():
    # 报表生成逻辑
    pass
3.2.2 资源监控与阈值告警

对于关键任务,可以配合psutil库监控系统资源状态:

python 复制代码
import psutil

def check_system_health():
    cpu_percent = psutil.cpu_percent(interval=1)
    memory_percent = psutil.virtual_memory().percent
    disk_percent = psutil.disk_usage('/').percent
    
    alerts = []
    if cpu_percent > 80:
        alerts.append(f"High CPU usage: {cpu_percent}%")
    if memory_percent > 85:
        alerts.append(f"High memory usage: {memory_percent}%")
    if disk_percent > 90:
        alerts.append(f"Low disk space: {disk_percent}% used")
    
    if alerts:
        logger.warning(f"System health alerts: {', '.join(alerts)}")
    
    return len(alerts) == 0

3.3 告警体系构建

3.3.1 分级告警设计

根据问题严重程度设计不同的告警通道和响应策略:

级别 触发条件 告警方式 响应时效
CRITICAL 任务完全失败、数据丢失 短信/电话 + 邮件 立即处理
ERROR 任务执行失败但可重试 邮件 + 即时消息 15分钟内
WARNING 性能下降、阈值超限 即时消息 按日汇总处理
INFO 正常执行完成 日志记录 无需响应
3.3.2 邮件告警实现
python 复制代码
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

def send_alert_email(subject, body, level='ERROR'):
    sender = 'alert@yourdomain.com'
    receivers = ['admin@yourdomain.com', 'oncall@yourdomain.com']
    
    msg = MIMEMultipart()
    msg['From'] = sender
    msg['To'] = ', '.join(receivers)
    msg['Subject'] = f'[{level}] {subject}'
    
    msg.attach(MIMEText(body, 'plain', 'utf-8'))
    
    try:
        with smtplib.SMTP('smtp.yourdomain.com', 587) as server:
            server.starttls()
            server.login(sender, 'password')
            server.send_message(msg)
        logger.info(f"Alert email sent: {subject}")
    except Exception as e:
        logger.error(f"Failed to send alert email: {e}")
3.3.3 告警收敛机制

避免告警风暴同样重要。可以设计以下收敛策略:

  • 告警分组:同一任务的相似告警合并为一条
  • 静默期:相同告警在一定时间窗口内只发送一次
  • 告警升级:未处理的告警自动升级通知层级

四、部署:让脚本"走"向目标机器

调度和监控解决了运行阶段的问题,但如何将写好的脚本交付到目标机器并稳定运行,同样不可忽视。

4.1 Python打包方案对比

Python打包成独立可执行文件的主流方案有以下几种:

工具 单文件支持 启动速度 体积 学习曲线 适用场景
PyInstaller 中等 中等偏大 通用应用,快速交付
Nuitka 中等 中-高 性能敏感、源码保护
cx_Freeze 偏onedir 中等 低-中 依赖较少的项目
PyOxidizer 小-中 深度定制场景

4.2 PyInstaller实战

PyInstaller是当前最流行的Python打包工具,上手快、生态成熟。

4.2.1 基础打包命令
bash 复制代码
# 安装
pip install pyinstaller

# 基础打包(生成单目录)
pyinstaller my_script.py

# 打包为单个EXE文件(-F)
pyinstaller -F my_script.py

# 无控制台窗口(GUI应用,-w)
pyinstaller -F -w my_script.py

# 添加图标(-i)
pyinstaller -F -i icon.ico my_script.py
4.2.2 处理隐式导入

有些库在打包时可能不会被PyInstaller自动检测到,需要在.spec文件中手动指定:

python 复制代码
# 编辑生成的.spec文件
a = Analysis(['my_script.py'],
             pathex=[],
             binaries=[],
             datas=[('config.yaml', '.')],
             hiddenimports=['pandas', 'numpy'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=None)
4.2.3 虚拟环境打包的最佳实践

为避免打包时引入不必要的依赖,建议在干净的虚拟环境中进行打包:

bash 复制代码
# 创建虚拟环境
python -m venv build_env
source build_env/bin/activate  # Linux/Mac
# 或 build_env\Scripts\activate  # Windows

# 安装项目依赖
pip install -r requirements.txt
pip install pyinstaller

# 执行打包
pyinstaller -F my_script.py

4.3 Nuitka:编译优化方案

对于性能敏感或需要源码保护的场景,Nuitka是更优的选择。Nuitka将Python代码转换为C++代码后编译为本地机器码,理论上可以获得更好的性能和源码保护效果。

bash 复制代码
# 安装
pip install nuitka

# 基本打包
nuitka --standalone --onefile my_script.py

# 带优化选项
nuitka --standalone --onefile --enable-plugin=tk-inter --windows-icon-from-ico=icon.ico my_script.py

Nuitka的打包速度相对较慢,因为它需要将Python代码编译成C++代码再编译为本机可执行文件。

4.4 环境变量与配置管理

打包后的程序通常需要在不同环境中运行,环境变量的管理变得尤为重要。

4.4.1 使用python-dotenv管理配置
python 复制代码
# .env文件
DATABASE_URL=postgresql://localhost:5432/mydb
API_KEY=your_api_key_here
LOG_LEVEL=INFO

# config.py
from dotenv import load_dotenv
import os

load_dotenv()

class Config:
    DATABASE_URL = os.getenv('DATABASE_URL')
    API_KEY = os.getenv('API_KEY')
    LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
4.4.2 配置类封装

为了更好的类型校验和错误处理,可以将环境变量封装为配置类:

python 复制代码
import os
from typing import Optional

class AppConfig:
    def __init__(self):
        self.database_url = self._get_env('DATABASE_URL')
        self.api_key = self._get_env('API_KEY')
        self.debug = self._get_env('DEBUG', 'false').lower() == 'true'
        self.max_retries = int(self._get_env('MAX_RETRIES', '3'))
    
    def _get_env(self, key: str, default: Optional[str] = None) -> str:
        value = os.getenv(key, default)
        if value is None:
            raise ValueError(f"Missing required environment variable: {key}")
        return value
    
    def validate(self):
        """验证配置完整性"""
        assert self.database_url, "DATABASE_URL is required"
        assert self.api_key, "API_KEY is required"
        return True

config = AppConfig()

4.5 代码签名与发布

对于公开发布的Windows可执行文件,代码签名是必要的。未签名的可执行文件更容易触发Windows SmartScreen警告与杀软误报,影响用户体验。

五、完整实战案例:一个可调度、可监控、可部署的自动化任务

让我们把所有知识点串联起来,构建一个完整的自动化任务系统。

5.1 项目结构

复制代码
auto_task/
├── config/
│   ├── __init__.py
│   ├── settings.py          # 配置管理
│   └── .env                 # 环境变量(不提交到版本控制)
├── core/
│   ├── __init__.py
│   ├── scheduler.py         # APScheduler调度器配置
│   ├── monitor.py           # 监控和日志模块
│   └── tasks.py             # 具体业务任务
├── logs/                    # 日志目录
├── requirements.txt         # 依赖清单
├── setup.py                 # 安装脚本
└── run.py                   # 主入口

5.2 核心代码实现

core/monitor.py - 监控模块

python 复制代码
import logging
import logging.handlers
import time
import uuid
from functools import wraps

def setup_logging():
    logger = logging.getLogger('auto_task')
    logger.setLevel(logging.DEBUG)
    
    # 控制台输出
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    
    # 文件输出(按天轮转)
    file_handler = logging.handlers.TimedRotatingFileHandler(
        'logs/task.log', when='midnight', backupCount=30
    )
    file_handler.setLevel(logging.DEBUG)
    
    formatter = logging.Formatter(
        '%(asctime)s - %(levelname)s - [%(name)s] - %(message)s'
    )
    console.setFormatter(formatter)
    file_handler.setFormatter(formatter)
    
    logger.addHandler(console)
    logger.addHandler(file_handler)
    return logger

logger = setup_logging()

def task_monitor(name):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            task_id = str(uuid.uuid4())[:8]
            start = time.time()
            logger.info(f"[{name}] Starting task {task_id}")
            try:
                result = func(*args, **kwargs)
                elapsed = time.time() - start
                logger.info(f"[{name}] Completed in {elapsed:.2f}s")
                return result
            except Exception as e:
                elapsed = time.time() - start
                logger.error(f"[{name}] Failed after {elapsed:.2f}s: {e}")
                raise
        return wrapper
    return decorator

core/scheduler.py - 调度器配置

python 复制代码
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': ThreadPoolExecutor(max_workers=10)
}

scheduler = BackgroundScheduler(
    jobstores=jobstores,
    executors=executors,
    timezone='Asia/Shanghai'
)

run.py - 主入口

python 复制代码
from core.scheduler import scheduler
from core.monitor import task_monitor, logger
from core.tasks import data_sync_task, report_generation_task

@task_monitor('data_sync')
def run_data_sync():
    data_sync_task()

@task_monitor('report')
def run_report():
    report_generation_task()

def main():
    # 配置调度任务
    scheduler.add_job(
        run_data_sync,
        'cron',
        hour=2,
        minute=0,
        id='data_sync',
        replace_existing=True
    )
    scheduler.add_job(
        run_report,
        'cron',
        hour=9,
        minute=30,
        id='daily_report',
        replace_existing=True
    )
    
    scheduler.start()
    logger.info("Scheduler started. Press Ctrl+C to exit.")
    
    try:
        while True:
            time.sleep(1)
    except (KeyboardInterrupt, SystemExit):
        scheduler.shutdown()
        logger.info("Scheduler stopped.")

if __name__ == '__main__':
    main()

5.3 部署清单

完成上述代码后,按照以下步骤完成部署:

  1. 环境准备:在目标机器上安装Python 3.8+,创建虚拟环境
  2. 安装依赖pip install -r requirements.txt
  3. 配置环境变量 :创建.env文件,填入数据库连接信息等敏感配置
  4. 测试运行python run.py手动测试
  5. 系统集成
    • Windows:使用任务计划程序配置开机自启
    • Linux:编写systemd service文件实现守护
  6. 打包发布(可选):使用PyInstaller或Nuitka打包为独立EXE
相关推荐
t***5442 小时前
Dev-C++ 中使用 Clang 调试有哪些常见问题
开发语言·c++
weixin_458580122 小时前
HTML函数工具是否适配HDR显示器_高动态范围指南【指南】
jvm·数据库·python
qq_654366982 小时前
Cgo 中正确设置 C 结构体内函数指针回调的完整方案
jvm·数据库·python
qq_432703662 小时前
如何处理复杂的SQL注入攻击_使用行为分析识别异常
jvm·数据库·python
sinat_383437362 小时前
如何在 Ubuntu Core(Snappy)上部署 Go Web 服务
jvm·数据库·python
遇见你的雩风2 小时前
Java --- 网络原理(三)
java·开发语言·网络
会编程的土豆2 小时前
Go语言零基础入门:从0到能写程序(超详细版)
开发语言·后端·golang
pele2 小时前
怎么诊断MongoDB Config Server响应极慢的问题_高频Auto-split导致的元库写入压力
jvm·数据库·python
itzixiao2 小时前
L1-058 6翻了(15分)[java][python]
java·开发语言·python·算法
小小码农Come on2 小时前
单例 QtObject 全局配置
开发语言·前端·javascript