execgo
<cite> **本文引用的文件** - [main.go](file://cmd/execgo/main.go) - [handler.go](file://internal/api/handler.go) - [task.go](file://internal/models/task.go) - [executor.go](file://internal/executor/executor.go) - [http.go](file://internal/executor/http.go) - [shell.go](file://internal/executor/shell.go) - [file.go](file://internal/executor/file.go) - [scheduler.go](file://internal/scheduler/scheduler.go) - [state.go](file://internal/state/state.go) - [observability.go](file://internal/observability/observability.go) - [README.md](file://README.md) </cite>

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖分析
  7. 性能考虑
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本文件面向 ExecGo API 的使用者与集成者,系统性梳理错误处理机制与 HTTP 状态码规范,覆盖请求解析、任务验证、执行器选择、任务执行、状态更新与持久化等全流程中的错误场景与响应格式。同时给出客户端错误处理最佳实践,帮助区分可恢复与永久性错误,并提供重试、超时与错误恢复策略建议。

项目结构

ExecGo 采用分层架构:入口程序负责初始化与 HTTP 服务;API 层负责路由与错误响应;调度器负责 DAG 任务的并发执行与状态推进;执行器负责具体任务的执行;状态管理器负责内存与持久化;可观测性模块提供日志、追踪与指标。

graph TB
subgraph "入口"
M["main.go"]
end
subgraph "API 层"
H["handler.go"]
end
subgraph "调度器"
S["scheduler.go"]
end
subgraph "执行器"
E1["http.go"]
E2["shell.go"]
E3["file.go"]
ER["executor.go"]
end
subgraph "状态管理"
ST["state.go"]
end
subgraph "可观测性"
O["observability.go"]
end
subgraph "模型"
T["task.go"]
end
M --> H
H --> S
S --> ER
ER --> E1
ER --> E2
ER --> E3
S --> ST
H --> O
S --> O
ST --> O

图表来源

  • main.go:64-70
  • handler.go:39-52
  • scheduler.go:18-45
  • executor.go:14-67
  • http.go:22-75
  • shell.go:31-79
  • file.go:20-113
  • state.go:17-53
  • observability.go:69-80
  • task.go:21-39

章节来源

  • README.md:32-57
  • main.go:64-70
  • handler.go:39-52

核心组件

  • API 层:统一处理 HTTP 请求,进行 JSON 解析、任务图验证、执行器类型检查,并在错误时返回标准错误响应与相应状态码。
  • 调度器:接收任务图,构建依赖关系,按并发上限调度执行,处理重试与超时,推进状态并级联下游。
  • 执行器:HTTP、Shell、File 三种内置执行器,均在参数解析失败或执行失败时返回错误信息。
  • 状态管理:提供任务的增删改查、状态原子更新与持久化。
  • 模型:定义任务、任务图、统一错误响应等数据结构。
  • 可观测性:提供结构化日志、请求追踪(traceID)与指标。

章节来源

  • handler.go:58-99
  • scheduler.go:69-97
  • executor.go:14-67
  • state.go:55-108
  • task.go:123-149
  • observability.go:69-80

架构总览

下图展示 API 层到调度器与执行器的关键调用链路,以及错误在各层的传播与响应方式。

sequenceDiagram
participant C as "客户端"
participant API as "API 层(handler.go)"
participant SCH as "调度器(scheduler.go)"
participant REG as "执行器注册表(executor.go)"
participant EXE as "执行器(HTTP/Shell/File)"
participant ST as "状态(state.go)"
C->>API : "POST /tasks"
API->>API : "解析JSON/校验任务图/检查执行器类型"
API->>SCH : "Submit(任务图)"
SCH->>ST : "Put/UpdateStatus"
SCH->>REG : "Get(任务类型)"
REG-->>SCH : "执行器实例或错误"
SCH->>EXE : "Execute(带超时/重试)"
EXE-->>SCH : "结果或错误"
SCH->>ST : "UpdateStatus(成功/失败/跳过)"
API-->>C : "202 Accepted + SubmitResponse 或错误响应"

图表来源

  • handler.go:58-99
  • scheduler.go:69-97
  • executor.go:38-48
  • http.go:27-75
  • shell.go:36-79
  • file.go:25-52
  • state.go:94-108

详细组件分析

API 层错误处理与状态码

  • POST /tasks
    • 无效 JSON:返回 400,错误响应体包含 error 字段,提示“invalid JSON”及底层错误详情。
    • 任务图验证失败:返回 400,错误响应体包含具体验证错误信息。
    • 未知任务类型:返回 400,错误响应体包含“unknown task type”及可用类型列表。
    • 成功提交:返回 202 Accepted,响应体包含 accepted 数量与 task_ids 列表。
  • GET /tasks/{id}
    • 任务不存在:返回 404,错误响应体包含“task not found”。
  • DELETE /tasks/{id}
    • 任务不存在:返回 404,错误响应体包含“task not found”。
    • 成功删除:返回 204 No Content。
  • GET /tasks
    • 返回 200,列出所有任务。
  • GET /health
    • 返回 200,包含健康状态、版本与运行时长。
  • GET /metrics
    • 返回 200,包含任务总数、运行中、成功、失败与按类型统计。

章节来源

  • handler.go:58-99
  • handler.go:101-126
  • handler.go:128-146
  • task.go:129-132

任务图验证与错误

  • 任务图为空:返回 400。
  • 任务缺少 id/type:返回 400。
  • 重复 id:返回 400。
  • 依赖引用不存在或自依赖:返回 400。
  • 检测到环:返回 400。
  • 通过验证后,API 层会逐项检查执行器是否存在,不存在则返回 400。

章节来源

  • task.go:41-79
  • handler.go:70-85

执行器错误与状态推进

  • HTTP 执行器
    • 参数解析失败:返回错误,调度器将其记录为失败。
    • URL/Method 缺失:返回错误,调度器将其记录为失败。
    • 请求失败:返回错误,调度器将其记录为失败。
    • 即使 HTTP 状态码 ≥ 400,仍返回结果但标记为失败。
  • Shell 执行器
    • 参数解析失败:返回错误。
    • 命令不在白名单:返回错误。
    • 命令执行失败:返回结果(含 stdout/stderr/exit_code)并标记为失败。
  • File 执行器
    • 参数解析失败:返回错误。
    • 路径缺失:返回错误。
    • 不支持的动作:返回错误。
    • 文件读写/删除/状态查询失败:返回错误。

章节来源

  • http.go:27-75
  • shell.go:36-79
  • file.go:25-113
  • scheduler.go:127-190

状态管理与持久化

  • Put/Get/GetAll/Delete/UpdateStatus 提供原子更新与并发安全。
  • 运行中任务在恢复时被重置为 pending,避免悬挂状态。
  • 定期持久化与最终持久化保证崩溃后可恢复。

章节来源

  • state.go:55-108
  • state.go:160-179

统一错误响应格式

  • 统一错误响应体包含一个字符串字段 error,用于描述错误原因。
  • 响应头 Content-Type 设置为 application/json。
  • API 层在错误时直接返回该格式,不包裹额外容器。

章节来源

  • task.go:129-132
  • handler.go:152-156

可恢复与永久性错误

  • 可恢复错误
    • 网络波动、临时性外部服务不可达、超时、执行器内部瞬时失败。
    • 调度器对任务执行采用指数退避重试(最多 retry+1 次),并在每次尝试前根据超时上下文控制执行时间。
  • 永久性错误
    • 任务图验证失败(如缺少必要字段、自依赖、环依赖等)。
    • 未知任务类型或执行器未注册。
    • Shell 命令不在白名单。
    • File 执行器动作不支持或路径非法。
    • 任务不存在(查询/删除)。
  • 区分建议
    • 对于 4xx(客户端错误)与 422(语义错误)类响应,通常为永久性错误,需修正请求后再提交。
    • 对于 5xx(服务端错误)与网络/超时导致的失败,可结合指数退避重试。

章节来源

  • scheduler.go:144-180
  • http.go:70-74
  • shell.go:52-54
  • file.go:49-51
  • handler.go:64-85

依赖分析

  • API 层依赖调度器与状态管理器,通过统一的错误响应格式对外输出。
  • 调度器依赖执行器注册表与执行器实现,负责状态推进与持久化。
  • 执行器实现各自参数解析与执行逻辑,失败时返回错误信息。
  • 可观测性模块贯穿各层,提供日志与追踪。
graph LR
API["API 层(handler.go)"] --> SCH["调度器(scheduler.go)"]
API --> ST["状态(state.go)"]
SCH --> REG["执行器注册表(executor.go)"]
REG --> EXE_HTTP["HTTP 执行器(http.go)"]
REG --> EXE_SHELL["Shell 执行器(shell.go)"]
REG --> EXE_FILE["File 执行器(file.go)"]
API --> OBS["可观测性(observability.go)"]
SCH --> OBS
ST --> OBS

图表来源

  • handler.go:39-52
  • scheduler.go:18-45
  • executor.go:14-67
  • http.go:22-75
  • shell.go:31-79
  • file.go:20-113
  • state.go:17-53
  • observability.go:69-80

性能考虑

  • 并发控制:调度器通过信号量限制最大并发,避免资源争用。
  • 就绪队列:使用带缓冲通道承载就绪任务,减少锁竞争。
  • 指数退避:重试间隔按 100ms·2^(attempt-2) 增长,上限 10 秒,降低抖动。
  • 超时控制:每个执行尝试基于任务 timeout 构造带超时的上下文,避免长时间阻塞。
  • 持久化:定期持久化与最终持久化确保崩溃恢复,避免丢失状态。

章节来源

  • scheduler.go:47-58
  • scheduler.go:99-107
  • scheduler.go:144-180
  • state.go:160-179

故障排查指南

  • 常见错误与定位
    • 400 invalid JSON:检查请求体是否为合法 JSON,确认字段拼写与类型。
    • 400 验证失败:根据错误信息修正任务图,确保 id、type、依赖引用合法且无环。
    • 400 unknown task type:确认任务类型存在于已注册执行器列表中。
    • 404 任务不存在:确认任务 ID 是否正确,或是否已被删除。
    • 5xx 服务端错误:查看服务端日志(结构化 JSON),关注 trace_id 定位请求链路。
  • 日志与追踪
    • API 层与调度器均使用结构化日志,包含 trace_id,便于跨组件关联。
    • 可通过 /metrics 查看任务总量、运行中、成功、失败与按类型统计。
  • 恢复与重启
    • 重启后运行中任务会被重置为 pending,确保一致性。
    • 若持久化文件损坏,可删除后重新提交任务图重建状态。

章节来源

  • handler.go:64-85
  • handler.go:101-126
  • scheduler.go:127-190
  • state.go:41-50
  • state.go:136-158
  • observability.go:50-63

结论

ExecGo 的错误处理遵循“明确、一致、可观测”的原则:API 层统一返回 400/404/202/200 等状态码与标准化错误响应;调度器与执行器在失败时提供清晰的错误信息;可观测性贯穿始终,便于问题定位与恢复。客户端应区分可恢复与永久性错误,采用指数退避重试与合理超时策略,以提升整体鲁棒性。

附录

HTTP 状态码与含义

  • 200 OK
    • GET /tasks:返回所有任务列表。
    • GET /health:返回健康检查信息。
    • GET /metrics:返回指标信息。
  • 201 Created
    • 本项目未实现该状态码。
  • 202 Accepted
    • POST /tasks:任务图已接收并进入调度队列,返回 accepted 数量与 task_ids。
  • 204 No Content
    • DELETE /tasks/{id}:删除成功。
  • 400 Bad Request
    • 请求体非 JSON 或任务图验证失败。
    • 未知任务类型(包含可用类型列表)。
  • 404 Not Found
    • 查询或删除的任务不存在。
  • 500 Internal Server Error
    • 本项目未显式返回该状态码;若出现,通常为未捕获异常,建议查看服务端日志。

章节来源

  • handler.go:58-99
  • handler.go:101-126
  • handler.go:128-146

错误响应格式

  • 字段
    • error: 字符串,描述错误原因。
  • 示例
    • 400 invalid JSON:包含 invalid JSON 与底层错误详情。
    • 400 验证失败:包含具体验证错误信息。
    • 400 unknown task type:包含 unknown task type 与可用类型列表。
    • 404 任务不存在:包含 task not found 与任务 ID。

章节来源

  • task.go:129-132
  • handler.go:64-85

客户端错误处理最佳实践

  • 重试策略
    • 指数退避:首次延迟 100ms,后续按 2 倍增长,上限 10 秒。
    • 最大重试次数:retry 字段决定最大尝试次数(至少 1 次)。
    • 超时控制:为每次重试设置合理的超时上下文,避免长时间阻塞。
  • 超时设置
    • 读取超时:15 秒。
    • 写入超时:30 秒。
    • 空闲连接超时:60 秒。
  • 错误恢复
    • 对于 400/404:修正请求后重试。
    • 对于 5xx/网络错误:指数退避重试,超过阈值后回退至人工干预。
    • 使用 trace_id 在日志中定位问题,结合 /metrics 观察趋势。

章节来源

  • main.go:64-70
  • scheduler.go:144-180
  • observability.go:50-63