文章目录
函数签名
func HandleCreateJobTask(ctx context.Context, task *asynq.Task) error
- 功能:处理来自异步队列的创建作业任务
- 输入:
ctx context.Context
:携带链路跟踪信息的上下文task *asynq.Task
:异步任务对象(使用asynq库)
- 输出:错误信息(决定任务是否需要重试)
核心处理流程
关键代码解析
1. 上下文与载荷解析
ctx = NewTaskContext(ctx, task.Type()) // 创建任务类型上下文
payload := &CreateJobPayload{}
err := json.Unmarshal(task.Payload(), payload) // 解析任务载荷
- NewTaskContext:扩展上下文,添加任务类型标记(用于日志跟踪)
- Payload解析:将异步任务携带的JSON数据反序列化为结构体
2. 数据库事务
err = dao.TaskDao.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
// 事务操作
})
- 事务范围:包裹所有数据库操作,保障原子性
- LockUpdate():使用
SELECT ... FOR UPDATE
锁定记录,防止并发修改
3. 主任务校验
one, err := dao.TaskDao.Ctx(ctx).LockUpdate().
Where(columns.TaskId, payload.TaskId).
Where(columns.Status, types.DataStatusActive).One()
- 双重校验:
- 检查任务是否存在(
one.IsEmpty()
) - 检查任务状态是否允许操作(
util.ArrayContains
校验非法状态)
- 检查任务是否存在(
4. 存储配置补全
err = FillStorageInfo(ctx, taskInfo) // 补充存储详细信息
- 业务需求:主任务仅存储存储标识,需要补全完整存储配置
- 潜在实现:可能查询存储服务或配置中心获取详细信息
5. 子任务处理
all, err := dao.SubTaskDao.Ctx(ctx).
Where(columns.TaskId, payload.TaskId).
Where(columns.Status, types.DataStatusActive).All()
- 关联查询:获取所有有效的子任务记录
- 空值检查:
all.IsEmpty()
确保至少存在一个子任务
6. 作业工厂模式
jobFactory, err := job.NewJobFactory(taskInfo, subTasks)
err = jobFactory.Validate(ctx) // 参数校验
resources, err := jobFactory.GenerateResources(ctx) // 生成K8s资源
- 工厂模式:隔离资源生成逻辑
- 生成内容:可能包含Deployment/Job/Service等K8s资源描述
7. 资源提交
err = createJobResource(ctx, taskInfo.ClusterId, resources)
- 集群操作:根据任务指定的集群ID(
ClusterId
)提交资源 - 潜在实现:使用Kubernetes client-go库与指定集群API交互
事务管理策略
操作阶段 | 是否影响事务 | 说明 |
---|---|---|
数据库查询 | ✅ 是 | 使用事务上下文执行 |
外部存储查询 | ❌ 否 | FillStorageInfo可能涉及外部调用 |
K8s资源创建 | ❌ 否 | 发生在事务提交前 |
关键注意点:
K8s资源创建操作不在事务保护范围内,可能造成:
- 数据库事务提交失败但资源已创建 → 需要补偿机制
- 资源创建失败但事务已提交 → 需任务重试机制
错误处理设计
错误分类
错误类型 | 处理方式 | 重试策略 |
---|---|---|
数据校验失败(如非法状态) | 记录警告日志,返回错误 | 不重试 |
数据库连接失败 | 记录错误日志,返回错误 | 自动重试 |
K8s API调用失败 | 记录错误日志,返回错误 | 自动重试 |
重试机制
- asynq特性:默认自动重试(指数退避)
- 幂等设计:
- 通过
LockUpdate
防止并发修改 - 资源创建需支持幂等(如检查资源已存在)
- 通过
潜在改进建议
- 补偿事务设计
// 在事务提交后添加后续操作
tx.AfterCommit(func() {
if err := createJobResource(...); err != nil {
// 启动补偿任务回滚数据库
}
})
- 增加 tracing
// 添加分布式追踪
span, ctx := opentracing.StartSpanFromContext(ctx, "create_job")
defer span.Finish()
- 资源预检
// 提交前检查集群可用性
if err := checkClusterHealth(taskInfo.ClusterId); err != nil {
return fmt.Errorf("cluster unavailable: %v", err)
}
典型执行场景
// 任务入队示例
client.Enqueue(asynq.NewTask("create_job", payload))
// 工作流示意图
TaskCreated → 消息入队 → 消费者处理 → 数据库操作 → K8s资源创建
^ |
|-----------------|
重试机制
这个实现展示了典型的任务队列处理模式,结合数据库事务与外部系统操作,需要注意分布式场景下的数据一致性保障。