一、引言:为什么你需要一个完整的自动化体系?
如果你写过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_date和end_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 创建基本任务(向导模式)
对于新手,推荐使用"创建基本任务"向导模式:
-
命名和描述:建议认真填写"名称"和"描述"。例如,名称可以叫"每日销售报表生成",描述里写上"每天上午9点运行,调用daily_report.py"。好的描述能让你(或你的同事)在维护时快速理解任务用途。
-
选择触发器:可选每日、每周、每月、一次性、计算机启动时、用户登录时等。
-
设置操作 :选择"启动程序",在"程序或脚本"中填写Python解释器的完整路径(如
C:\Python39\python.exe),在"添加参数"中填写脚本的完整路径(如D:\scripts\daily_report.py),在"起始于"中填写脚本所在目录。 -
完成设置:检查所有配置后点击"完成"。
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 部署清单
完成上述代码后,按照以下步骤完成部署:
- 环境准备:在目标机器上安装Python 3.8+,创建虚拟环境
- 安装依赖 :
pip install -r requirements.txt - 配置环境变量 :创建
.env文件,填入数据库连接信息等敏感配置 - 测试运行 :
python run.py手动测试 - 系统集成 :
- Windows:使用任务计划程序配置开机自启
- Linux:编写systemd service文件实现守护
- 打包发布(可选):使用PyInstaller或Nuitka打包为独立EXE