桌面端自动化任务系统设计:资源池、代理健康检查、任务调度与审计日志
桌面端自动化任务通常需要连续运行数小时甚至数天。只要系统涉及多个执行单元、不同网络出口、周期任务和失败重试,简单的“启动一个线程执行函数”很快就会遇到资源冲突、状态丢失、重复执行和问题难以追踪等情况。
本文给出一种适用于 Windows 桌面应用的自动化任务系统设计。目标是让任务可调度、资源可隔离、失败可恢复、运行过程可审计,同时保持本地部署的简单性。
一、先定义系统目标
一个可靠的桌面端任务系统至少要满足四个条件:
1. 资源隔离:同一执行单元在同一时间只能被一个任务租用。
2. 状态可见:界面能够显示等待、运行、冷却、失败和完成等状态。
3. 失败可恢复:程序退出或网络中断后,可以从持久化检查点继续。
4. 过程可审计:每次状态变化、网络请求和重试都留下结构化记录。
如果没有这四个基础能力,任务数量增加后,系统只能依赖人工观察和反复重启。
二、分层架构
推荐把系统拆成五层:
资源层:维护执行单元、网络出口和本地凭据的状态。
调度层:选择可用资源,控制并发、优先级和运行时间窗口。
执行层:运行具体业务步骤,并上报进度与结果。
存储层:保存任务、检查点、事件日志和统计数据。
界面层:只消费稳定状态,不直接控制底层线程。
界面层和执行层分离非常重要。用户关闭弹窗或切换页面时,不应该影响后台任务生命周期。
三、资源池与租约机制
资源池不能只使用一个布尔字段表示“是否占用”。更可靠的模型是租约:
resource_id:资源唯一标识
state:AVAILABLE、LEASED、RUNNING、COOLDOWN、FAULT、DISABLED
lease_owner:当前任务标识
lease_until:租约到期时间
route_id:绑定的网络出口
last_error:最近一次错误
updated_at:最后更新时间
任务申请资源时,通过数据库事务执行条件更新:
UPDATE resource
SET state = 'LEASED',
lease_owner = :task_id,
lease_until = :expires_at
WHERE resource_id = :id
AND state = 'AVAILABLE';
只有受影响行数为 1 时才表示租约成功。这样即使多个工作线程同时申请,也不会重复占用同一资源。
租约还解决了进程异常退出的问题。系统重启后,可以扫描已经过期的 LEASED 或 RUNNING 记录,根据检查点恢复或回收资源。
四、状态机设计
不要在不同模块中随意修改状态。应把允许的转换写成明确规则:
AVAILABLE -> LEASED
LEASED -> RUNNING
RUNNING -> COOLDOWN
RUNNING -> FAULT
COOLDOWN -> AVAILABLE
FAULT -> AVAILABLE
FAULT -> DISABLED
每次转换都需要校验当前状态、记录原因并写入事件表。非法转换必须拒绝,例如一个已经 DISABLED 的资源不能直接进入 RUNNING。
状态机还应区分“任务失败”和“资源失败”。业务参数错误属于任务失败,网络出口不可用属于资源失败。两类问题采用不同恢复策略。
五、代理健康检查
网络出口不应只记录“可用”或“不可用”。可以维护以下指标:
最近检测时间
连接延迟
连续成功次数
连续失败次数
最近错误类型
冷却截止时间
健康检查建议分成三个阶段:
第一阶段检查 TCP 连接是否建立;
第二阶段访问轻量级探测地址;
第三阶段验证出口地址和预期地区是否一致。
调度器可以根据健康分数选择出口,例如:
score = 100
- latency_ms / 20
- consecutive_failures * 15
- recent_timeout_count * 10
分数低于阈值时进入 COOLDOWN,而不是永久禁用。冷却期结束后再进行一次探测,通过后回到可用状态。
六、任务调度器
调度器的职责不是执行具体业务,而是回答四个问题:
哪个任务应该先运行?
需要多少资源?
当前是否处于允许的时间窗口?
失败后何时重试?
任务表可以保存 priority、scheduled_at、max_concurrency、retry_count、next_retry_at 和 checkpoint。调度循环只拉取到期且状态为 WAITING 的任务。
为了避免瞬时并发,可以同时使用:
全局并发上限
同类任务并发上限
单资源冷却时间
固定间隔加随机抖动
按小时统计的运行预算
随机抖动不应用来掩盖无序行为,它的作用是避免所有工作线程在同一毫秒竞争数据库和网络资源。
七、重试必须分类
所有异常都立即重试,是自动化系统中常见的错误。建议将错误分为三类:
可重试错误:临时超时、连接重置、服务短暂不可用。
延迟重试错误:触发频率限制、出口进入冷却期。
不可重试错误:参数非法、权限不足、目标不存在。
指数退避可以使用:
delay = min(base * 2 ^ retry_count, max_delay)
同时加入少量随机抖动。超过最大次数后,任务进入 NEEDS_REVIEW,等待人工检查,而不是无限循环。
八、检查点与幂等性
长任务需要在每个可恢复步骤后写入检查点。检查点至少包含:
当前阶段
最后处理的项目标识
累计成功数和失败数
资源租约信息
下次恢复所需参数
执行步骤还应具备幂等性。可以为每个业务动作生成 operation_key,并在提交前检查是否已经成功处理。这样程序崩溃后重新运行,不会重复执行已完成动作。
九、结构化审计日志
普通文本日志适合开发调试,但不适合界面查询和统计。建议建立 event_log 表:
event_id
task_id
resource_id
event_type
level
message
payload_json
created_at
事件类型可以包括 TASK_CREATED、RESOURCE_LEASED、STEP_STARTED、STEP_SUCCEEDED、RETRY_SCHEDULED、RESOURCE_RELEASED 和 TASK_FINISHED。
payload_json 只保存诊断需要的数据,不应记录密码、令牌或完整凭据。敏感字段应在写入前统一脱敏。
十、本地存储与备份
桌面应用可以使用 SQLite 保存任务和日志,并启用 WAL 模式降低读写竞争。数据库写入应由统一仓储层完成,避免多个界面组件直接拼接 SQL。
备份时不能只复制正在写入的数据库文件。应使用 SQLite 在线备份接口,或先完成检查点和安全暂停,再生成备份副本。恢复后还要扫描过期租约,保证状态一致。
十一、测试策略
任务系统的测试重点不是界面点击,而是状态转换和故障恢复:
两个任务同时申请同一资源,确保只有一个成功;
执行过程中模拟进程退出,验证检查点恢复;
让网络出口连续超时,验证冷却和重新探测;
重复提交相同 operation_key,验证幂等性;
构造不可重试错误,确保不会进入无限循环;
数据库重启后,验证过期租约可以被回收。
时间相关逻辑最好注入可控制的时钟,避免测试依赖真实等待。
十二、可观察性指标
除了成功率,还应关注:
任务排队时长
平均执行时长
资源利用率
重试率
冷却资源数量
各错误类型占比
从失败到恢复的平均时间
这些指标能够帮助判断瓶颈来自资源不足、网络质量、调度策略还是业务步骤本身。
结语
可靠的桌面端自动化系统,本质上是一个小型任务平台。资源池解决并发占用,状态机约束生命周期,健康检查隔离不稳定网络,调度器控制并发和重试,检查点保证恢复,审计日志负责解释系统行为。
先把状态、租约、幂等性和日志设计清楚,再扩展具体任务类型,系统会比直接增加线程和定时器更容易维护。
