681 lines
16 KiB
Go
681 lines
16 KiB
Go
package transcode
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"log"
|
||
"runtime/debug"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// Task 任务定义
|
||
type Task struct {
|
||
ID string // 任务唯一ID
|
||
Type string // 任务类型
|
||
EncodeUrl string // 任务音频地址
|
||
execute func(ctx context.Context, task *Task) error // 执行函数
|
||
ctx context.Context // 任务上下文
|
||
cancelFunc context.CancelFunc // 取消函数
|
||
status TaskStatus // 任务状态
|
||
startTime time.Time // 开始时间
|
||
endTime time.Time // 结束时间
|
||
createTime time.Time // 创建时间
|
||
quit chan error
|
||
success chan struct{}
|
||
isEnd bool
|
||
Duration int64
|
||
}
|
||
|
||
type TaskInfo struct {
|
||
Status TaskStatus // 任务状态
|
||
StartTime time.Time // 开始时间
|
||
EndTime time.Time // 结束时间
|
||
CreateTime time.Time // 创建时间
|
||
}
|
||
|
||
// TaskStatus 任务状态
|
||
type TaskStatus int
|
||
|
||
const (
|
||
TaskPending TaskStatus = iota // 等待中
|
||
TaskRunning // 执行中
|
||
TaskCompleted // 已完成
|
||
TaskCancelled // 已取消
|
||
TaskFailed // 已失败
|
||
)
|
||
|
||
// Workflow 工作流
|
||
type Workflow struct {
|
||
maxConcurrency int // 最大并发数
|
||
semaphore chan struct{} // 并发信号量
|
||
tasks map[string]*Task // 所有任务
|
||
taskQueue map[string][]string // 按类型分组的任务队列
|
||
runningTasks map[string]string // 正在运行的任务类型 -> 任务ID
|
||
mu sync.RWMutex // 读写锁
|
||
wg sync.WaitGroup // 等待组
|
||
cleanupTicker *time.Ticker // 清理定时器
|
||
cleanupStop chan struct{} // 清理停止信号
|
||
|
||
// 清理配置
|
||
cleanupInterval time.Duration // 清理间隔
|
||
maxTaskHistory int // 最大历史任务数
|
||
retentionTime time.Duration // 任务保留时间
|
||
|
||
// 回调函数
|
||
onComplete func(task *Task) // 完成回调
|
||
onCancel func(task *Task) // 取消回调
|
||
onError func(task *Task, err error) // 错误回调
|
||
onCleanup func(task *Task, reason string) // 清理回调
|
||
}
|
||
|
||
// WorkflowConfig 工作流配置
|
||
type WorkflowConfig struct {
|
||
MaxConcurrency int // 最大并发数,默认2
|
||
CleanupInterval time.Duration // 清理间隔,默认30秒
|
||
MaxTaskHistory int // 最大历史任务数,默认1000
|
||
RetentionTime time.Duration // 任务保留时间,默认5分钟
|
||
}
|
||
|
||
// NewWorkflow 创建工作流
|
||
func NewWorkflow(config WorkflowConfig) *Workflow {
|
||
if config.MaxConcurrency <= 0 {
|
||
config.MaxConcurrency = 2
|
||
}
|
||
if config.CleanupInterval <= 0 {
|
||
config.CleanupInterval = 30 * time.Second
|
||
}
|
||
if config.MaxTaskHistory <= 0 {
|
||
config.MaxTaskHistory = 1000
|
||
}
|
||
if config.RetentionTime <= 0 {
|
||
config.RetentionTime = 5 * time.Minute
|
||
}
|
||
|
||
wf := &Workflow{
|
||
maxConcurrency: config.MaxConcurrency,
|
||
semaphore: make(chan struct{}, config.MaxConcurrency),
|
||
tasks: make(map[string]*Task),
|
||
taskQueue: make(map[string][]string),
|
||
runningTasks: make(map[string]string),
|
||
cleanupInterval: config.CleanupInterval,
|
||
maxTaskHistory: config.MaxTaskHistory,
|
||
retentionTime: config.RetentionTime,
|
||
cleanupStop: make(chan struct{}),
|
||
}
|
||
|
||
// 启动清理协程
|
||
wf.startCleanupRoutine()
|
||
|
||
return wf
|
||
}
|
||
|
||
// SetCallbacks 设置回调函数
|
||
func (wf *Workflow) SetCallbacks(
|
||
onComplete func(task *Task),
|
||
onCancel func(task *Task),
|
||
onError func(task *Task, err error),
|
||
onCleanup func(task *Task, reason string),
|
||
) {
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
wf.onComplete = onComplete
|
||
wf.onCancel = onCancel
|
||
wf.onError = onError
|
||
wf.onCleanup = onCleanup
|
||
}
|
||
|
||
// AddTask 添加新任务
|
||
func (wf *Workflow) AddTask(task *Task) error {
|
||
if task == nil {
|
||
return errors.New("task cannot be nil")
|
||
}
|
||
|
||
if task.ID == "" {
|
||
return errors.New("task ID cannot be empty")
|
||
}
|
||
|
||
if task.execute == nil {
|
||
return errors.New("task execute function cannot be nil")
|
||
}
|
||
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
// 检查任务是否已存在
|
||
if _, exists := wf.tasks[task.ID]; exists {
|
||
return fmt.Errorf("task with ID %s already exists", task.ID)
|
||
}
|
||
|
||
// 设置任务初始状态
|
||
task.status = TaskPending
|
||
task.ctx, task.cancelFunc = context.WithCancel(context.Background())
|
||
task.createTime = time.Now()
|
||
|
||
// 保存任务到map
|
||
wf.tasks[task.ID] = task
|
||
|
||
// 添加到类型队列
|
||
if _, ok := wf.taskQueue[task.Type]; !ok {
|
||
wf.taskQueue[task.Type] = make([]string, 0)
|
||
}
|
||
wf.taskQueue[task.Type] = append(wf.taskQueue[task.Type], task.ID)
|
||
|
||
// 尝试执行任务(在锁外执行,避免死锁)
|
||
go func() {
|
||
wf.tryExecuteTask(task.Type)
|
||
}()
|
||
|
||
return nil
|
||
}
|
||
|
||
// tryExecuteTask 尝试执行任务
|
||
func (wf *Workflow) tryExecuteTask(taskType string) {
|
||
wf.mu.Lock()
|
||
|
||
// 检查是否有该类型的任务正在运行
|
||
if _, isRunning := wf.runningTasks[taskType]; isRunning {
|
||
wf.mu.Unlock()
|
||
return
|
||
}
|
||
|
||
// 获取该类型的任务队列
|
||
queue, exists := wf.taskQueue[taskType]
|
||
if !exists || len(queue) == 0 {
|
||
wf.mu.Unlock()
|
||
return
|
||
}
|
||
|
||
// 获取队列中的第一个任务
|
||
taskID := queue[0]
|
||
task, taskExists := wf.tasks[taskID]
|
||
if !taskExists || task.status != TaskPending {
|
||
wf.mu.Unlock()
|
||
return
|
||
}
|
||
|
||
wf.mu.Unlock()
|
||
|
||
// 尝试获取并发许可(非阻塞)
|
||
select {
|
||
case wf.semaphore <- struct{}{}:
|
||
// 获取成功,可以执行
|
||
wf.executeTask(task)
|
||
default:
|
||
// 并发已达上限,等待下次尝试
|
||
}
|
||
}
|
||
|
||
// executeTask 执行任务
|
||
func (wf *Workflow) executeTask(task *Task) {
|
||
wf.mu.Lock()
|
||
|
||
// 再次检查任务状态
|
||
if task.status != TaskPending {
|
||
wf.mu.Unlock()
|
||
<-wf.semaphore // 释放信号量
|
||
return
|
||
}
|
||
|
||
// 更新任务状态
|
||
task.status = TaskRunning
|
||
task.startTime = time.Now()
|
||
|
||
// 记录该类型任务正在运行
|
||
wf.runningTasks[task.Type] = task.ID
|
||
|
||
// 从队列中移除(第一个元素)
|
||
if queue, exists := wf.taskQueue[task.Type]; exists && len(queue) > 0 {
|
||
wf.taskQueue[task.Type] = queue[1:]
|
||
}
|
||
|
||
wf.mu.Unlock()
|
||
|
||
wf.wg.Add(1)
|
||
|
||
// 启动任务执行
|
||
go func() {
|
||
defer func() {
|
||
// 恢复panic
|
||
if r := recover(); r != nil {
|
||
err := fmt.Errorf("task panic: %v\n%s", r, debug.Stack())
|
||
wf.handleTaskError(task, err)
|
||
}
|
||
|
||
wf.taskCompleted(task.Type)
|
||
wf.wg.Done()
|
||
}()
|
||
// 设置超时
|
||
timeNum := 12 * 60 * time.Minute
|
||
if task.Duration > 0 {
|
||
timeNum = time.Duration(task.Duration) * time.Second
|
||
}
|
||
ctx, cancel := context.WithTimeout(task.ctx, timeNum)
|
||
defer cancel()
|
||
|
||
// 执行任务
|
||
err := task.execute(ctx, task)
|
||
|
||
// 处理任务结果
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
if err != nil {
|
||
if errors.Is(err, context.Canceled) {
|
||
task.status = TaskCancelled
|
||
task.endTime = time.Now()
|
||
wf.triggerOnCancel(task)
|
||
} else {
|
||
task.status = TaskFailed
|
||
task.endTime = time.Now()
|
||
wf.triggerOnError(task, err)
|
||
}
|
||
} else {
|
||
task.status = TaskCompleted
|
||
task.endTime = time.Now()
|
||
wf.triggerOnComplete(task)
|
||
}
|
||
|
||
// 释放该类型的运行标记
|
||
delete(wf.runningTasks, task.Type)
|
||
}()
|
||
}
|
||
|
||
// taskCompleted 任务完成处理
|
||
func (wf *Workflow) taskCompleted(taskType string) {
|
||
// 释放并发许可
|
||
<-wf.semaphore
|
||
|
||
// 尝试执行下一个同类型任务
|
||
go wf.tryExecuteTask(taskType)
|
||
|
||
// 检查是否有其他类型的任务可以执行
|
||
wf.mu.RLock()
|
||
allTypes := make([]string, 0, len(wf.taskQueue))
|
||
for t := range wf.taskQueue {
|
||
allTypes = append(allTypes, t)
|
||
}
|
||
wf.mu.RUnlock()
|
||
|
||
for _, t := range allTypes {
|
||
if t != taskType {
|
||
go wf.tryExecuteTask(t)
|
||
}
|
||
}
|
||
}
|
||
|
||
// CancelTask 取消任务
|
||
func (wf *Workflow) CancelTask(taskID string) bool {
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
task, exists := wf.tasks[taskID]
|
||
if !exists {
|
||
return false
|
||
}
|
||
|
||
switch task.status {
|
||
case TaskPending:
|
||
// 从队列中移除
|
||
if queue, exists := wf.taskQueue[task.Type]; exists {
|
||
newQueue := make([]string, 0, len(queue))
|
||
for _, id := range queue {
|
||
if id != taskID {
|
||
newQueue = append(newQueue, id)
|
||
}
|
||
}
|
||
wf.taskQueue[task.Type] = newQueue
|
||
}
|
||
|
||
task.status = TaskCancelled
|
||
task.endTime = time.Now()
|
||
wf.triggerOnCancel(task)
|
||
return true
|
||
|
||
case TaskRunning:
|
||
// 取消正在执行的任务
|
||
if task.cancelFunc != nil {
|
||
task.cancelFunc()
|
||
}
|
||
return true
|
||
|
||
default:
|
||
// 已完成或已取消的任务
|
||
return false
|
||
}
|
||
}
|
||
|
||
// CleanupCompletedTasks 清理已完成的任务
|
||
func (wf *Workflow) CleanupCompletedTasks() int {
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
return wf.cleanupTasksInternal("manual", false)
|
||
}
|
||
|
||
// CleanupOldTasks 清理旧任务(基于保留时间)
|
||
func (wf *Workflow) CleanupOldTasks() int {
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
return wf.cleanupTasksInternal("retention", false)
|
||
}
|
||
|
||
// CleanupAllTasks 清理所有任务(谨慎使用)
|
||
func (wf *Workflow) CleanupAllTasks(force bool) int {
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
return wf.cleanupTasksInternal("all", force)
|
||
}
|
||
|
||
// cleanupTasksInternal 内部清理方法
|
||
func (wf *Workflow) cleanupTasksInternal(reason string, force bool) int {
|
||
cleanedCount := 0
|
||
now := time.Now()
|
||
|
||
for id, task := range wf.tasks {
|
||
// 跳过运行中和等待中的任务(除非强制清理)
|
||
if !force && (task.status == TaskRunning || task.status == TaskPending) {
|
||
continue
|
||
}
|
||
|
||
// 检查是否应该清理
|
||
shouldClean := false
|
||
|
||
switch reason {
|
||
case "manual":
|
||
// 手动清理:只清理已结束的任务
|
||
shouldClean = task.status == TaskCompleted ||
|
||
task.status == TaskCancelled ||
|
||
task.status == TaskFailed
|
||
|
||
case "retention":
|
||
// 基于保留时间清理
|
||
if task.endTime.IsZero() {
|
||
// 如果没有结束时间,使用创建时间
|
||
shouldClean = now.Sub(task.createTime) > wf.retentionTime
|
||
} else {
|
||
shouldClean = now.Sub(task.endTime) > wf.retentionTime
|
||
}
|
||
|
||
case "all":
|
||
// 清理所有任务(包括运行中和等待中的)
|
||
shouldClean = true
|
||
|
||
default:
|
||
// 默认清理已结束的任务
|
||
shouldClean = task.status == TaskCompleted ||
|
||
task.status == TaskCancelled ||
|
||
task.status == TaskFailed
|
||
}
|
||
|
||
if shouldClean {
|
||
// 触发清理回调
|
||
if wf.onCleanup != nil {
|
||
wf.onCleanup(task, reason)
|
||
}
|
||
|
||
// 从tasks map中删除
|
||
delete(wf.tasks, id)
|
||
cleanedCount++
|
||
}
|
||
}
|
||
|
||
// 清理空的任务队列
|
||
for taskType, queue := range wf.taskQueue {
|
||
newQueue := make([]string, 0, len(queue))
|
||
for _, taskID := range queue {
|
||
if _, exists := wf.tasks[taskID]; exists {
|
||
newQueue = append(newQueue, taskID)
|
||
}
|
||
}
|
||
wf.taskQueue[taskType] = newQueue
|
||
|
||
// 如果队列为空,删除该类型
|
||
if len(newQueue) == 0 {
|
||
delete(wf.taskQueue, taskType)
|
||
}
|
||
}
|
||
|
||
// 清理运行任务记录
|
||
for taskType, taskID := range wf.runningTasks {
|
||
if _, exists := wf.tasks[taskID]; !exists {
|
||
delete(wf.runningTasks, taskType)
|
||
}
|
||
}
|
||
|
||
return cleanedCount
|
||
}
|
||
|
||
// startCleanupRoutine 启动清理协程
|
||
func (wf *Workflow) startCleanupRoutine() {
|
||
wf.cleanupTicker = time.NewTicker(wf.cleanupInterval)
|
||
|
||
go func() {
|
||
for {
|
||
select {
|
||
case <-wf.cleanupTicker.C:
|
||
// 定期清理旧任务
|
||
wf.mu.Lock()
|
||
cleaned := wf.cleanupTasksInternal("retention", false)
|
||
wf.mu.Unlock()
|
||
|
||
if cleaned > 0 {
|
||
log.Printf("自动清理了 %d 个旧任务", cleaned)
|
||
}
|
||
|
||
// 检查任务数量,如果超过限制,清理最旧的任务
|
||
wf.mu.Lock()
|
||
if len(wf.tasks) > wf.maxTaskHistory {
|
||
wf.cleanupExcessTasks()
|
||
}
|
||
wf.mu.Unlock()
|
||
|
||
case <-wf.cleanupStop:
|
||
wf.cleanupTicker.Stop()
|
||
return
|
||
}
|
||
}
|
||
}()
|
||
}
|
||
|
||
// cleanupExcessTasks 清理超出限制的任务
|
||
func (wf *Workflow) cleanupExcessTasks() {
|
||
if len(wf.tasks) <= wf.maxTaskHistory {
|
||
return
|
||
}
|
||
|
||
// 收集所有任务并按创建时间排序
|
||
tasks := make([]*Task, 0, len(wf.tasks))
|
||
for _, task := range wf.tasks {
|
||
tasks = append(tasks, task)
|
||
}
|
||
|
||
// 按创建时间排序(最旧的在前面)
|
||
for i := 0; i < len(tasks); i++ {
|
||
for j := i + 1; j < len(tasks); j++ {
|
||
if tasks[j].createTime.Before(tasks[i].createTime) {
|
||
tasks[i], tasks[j] = tasks[j], tasks[i]
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计算需要清理的数量
|
||
excess := len(tasks) - wf.maxTaskHistory
|
||
|
||
// 清理最旧的任务(跳过运行中和等待中的任务)
|
||
cleaned := 0
|
||
for i := 0; i < len(tasks) && cleaned < excess; i++ {
|
||
task := tasks[i]
|
||
|
||
// 只清理已结束的任务
|
||
if task.status == TaskCompleted || task.status == TaskCancelled || task.status == TaskFailed {
|
||
if wf.onCleanup != nil {
|
||
wf.onCleanup(task, "max_history")
|
||
}
|
||
|
||
delete(wf.tasks, task.ID)
|
||
cleaned++
|
||
}
|
||
}
|
||
|
||
if cleaned > 0 {
|
||
log.Printf("清理了 %d 个任务以保持历史记录不超过 %d", cleaned, wf.maxTaskHistory)
|
||
}
|
||
}
|
||
|
||
// GetTaskStatus 获取任务状态
|
||
func (wf *Workflow) GetTaskStatus(taskID string) (TaskStatus, bool) {
|
||
wf.mu.RLock()
|
||
defer wf.mu.RUnlock()
|
||
|
||
task, exists := wf.tasks[taskID]
|
||
if !exists {
|
||
return TaskPending, false
|
||
}
|
||
|
||
return task.status, true
|
||
}
|
||
|
||
// GetTaskInfo 获取任务详情
|
||
func (wf *Workflow) GetTaskInfo(taskID string) (*TaskInfo, bool) {
|
||
wf.mu.RLock()
|
||
defer wf.mu.RUnlock()
|
||
|
||
task, exists := wf.tasks[taskID]
|
||
if !exists {
|
||
return &TaskInfo{}, false
|
||
}
|
||
return &TaskInfo{
|
||
Status: task.status,
|
||
EndTime: task.endTime,
|
||
CreateTime: task.createTime,
|
||
StartTime: task.startTime,
|
||
}, true
|
||
}
|
||
|
||
// GetRunningTasks 获取正在运行的任务
|
||
func (wf *Workflow) GetRunningTasks() []*Task {
|
||
wf.mu.RLock()
|
||
defer wf.mu.RUnlock()
|
||
|
||
result := make([]*Task, 0)
|
||
for _, task := range wf.tasks {
|
||
if task.status == TaskRunning {
|
||
result = append(result, task)
|
||
}
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// GetPendingTasks 获取等待中的任务
|
||
func (wf *Workflow) GetPendingTasks() []*Task {
|
||
wf.mu.RLock()
|
||
defer wf.mu.RUnlock()
|
||
|
||
result := make([]*Task, 0)
|
||
for _, task := range wf.tasks {
|
||
if task.status == TaskPending {
|
||
result = append(result, task)
|
||
}
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// GetPendingTasks 获取等待中的任务
|
||
func (wf *Workflow) GetIsTasks(bid string) bool {
|
||
wf.mu.RLock()
|
||
defer wf.mu.RUnlock()
|
||
|
||
result := false
|
||
for _, task := range wf.tasks {
|
||
if task.Type == bid && task.status != TaskPending && task.status != TaskRunning {
|
||
result = true
|
||
break
|
||
}
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// GetTaskCount 获取各种状态的任务数量
|
||
func (wf *Workflow) GetTaskCount() (total, running, pending, completed, cancelled, failed int) {
|
||
wf.mu.RLock()
|
||
defer wf.mu.RUnlock()
|
||
|
||
total = len(wf.tasks)
|
||
for _, task := range wf.tasks {
|
||
switch task.status {
|
||
case TaskRunning:
|
||
running++
|
||
case TaskPending:
|
||
pending++
|
||
case TaskCompleted:
|
||
completed++
|
||
case TaskCancelled:
|
||
cancelled++
|
||
case TaskFailed:
|
||
failed++
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// Wait 等待所有任务完成
|
||
func (wf *Workflow) Wait() {
|
||
wf.wg.Wait()
|
||
}
|
||
|
||
// Stop 停止工作流
|
||
func (wf *Workflow) Stop() {
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
// 发送清理停止信号
|
||
close(wf.cleanupStop)
|
||
|
||
// 取消所有任务
|
||
for _, task := range wf.tasks {
|
||
if task.status == TaskPending || task.status == TaskRunning {
|
||
if task.cancelFunc != nil {
|
||
task.cancelFunc()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 触发回调函数
|
||
func (wf *Workflow) triggerOnComplete(task *Task) {
|
||
if wf.onComplete != nil {
|
||
wf.onComplete(task)
|
||
}
|
||
}
|
||
|
||
func (wf *Workflow) triggerOnCancel(task *Task) {
|
||
if wf.onCancel != nil {
|
||
wf.onCancel(task)
|
||
}
|
||
}
|
||
|
||
func (wf *Workflow) triggerOnError(task *Task, err error) {
|
||
if wf.onError != nil {
|
||
wf.onError(task, err)
|
||
}
|
||
}
|
||
|
||
func (wf *Workflow) handleTaskError(task *Task, err error) {
|
||
wf.mu.Lock()
|
||
defer wf.mu.Unlock()
|
||
|
||
task.status = TaskFailed
|
||
task.endTime = time.Now()
|
||
wf.triggerOnError(task, err)
|
||
delete(wf.runningTasks, task.Type)
|
||
}
|