EasyVQD/internal/core/vqdtask/polling.go
2026-03-26 10:23:31 +08:00

214 lines
6.5 KiB
Go

package vqdtask
import (
"context"
"easyvqd/internal/core/vqd"
"easyvqd/pkg/vqdcms"
"fmt"
"log/slog"
"sync"
"time"
)
var (
VqdPollingTaskMap = vqdcms.VqdTaskMap{M: make(map[string]*vqdcms.VQDHandle)}
TaskFlowMap *TaskFlow
)
func (c *Core) StartPolling() {
TaskFlowMap = NewTaskFlow(int(c.Cfg.VqdConfig.PollingNum))
c.UpdatePollingTask()
// 注册开始执行回调
TaskFlowMap.RegisterStartCallback(func(task Task, executeDuration time.Duration, timeout time.Duration) {
slog.Debug("[全局回调-开始]", "任务", task.ID(), "预期耗时", executeDuration, "超时阈值", timeout)
// 可扩展:记录任务启动时间、初始化监控指标、打印执行前日志等
c.AddPollingTaskVqd(task.ID(), task.GetPara(), task.GetHandleInfo())
})
TaskFlowMap.RegisterSuccessCallback(func(taskID string, executeDuration time.Duration, elapsed time.Duration) {
slog.Debug("[全局回调-成功]", "任务", taskID, "预期耗时", executeDuration, "实际耗时", elapsed)
// 可扩展:记录日志、更新监控指标、发送通知等
c.DelPollingTaskVqd(taskID)
})
// 注册失败回调
TaskFlowMap.RegisterFailCallback(func(taskID string, err error, elapsed time.Duration) {
slog.Debug("[全局回调-失败]", "任务", taskID, "原因", err, "实际耗时", elapsed)
// 可扩展:记录错误日志、触发告警、重试策略等
c.DelPollingTaskVqd(taskID)
})
// 注册超时回调
TaskFlowMap.RegisterTimeoutCallback(func(taskID string, timeout time.Duration, elapsed time.Duration) {
slog.Debug("[全局回调-超时]", "任务", taskID, "超时阈值", timeout, "实际耗时", elapsed)
// 可扩展:发送超时告警、调整任务超时配置等
c.DelPollingTaskVqd(taskID)
})
items, _, err := c.VqdTaskCore.FindVqdPollingAll()
if err == nil && len(items) > 0 {
for _, v := range items {
errs := c.AddPollingTask(v)
if errs != nil {
slog.Error("vqd polling add template", "err", errs.Error())
continue
}
time.Sleep(100 * time.Millisecond)
}
}
// 启动任务流
TaskFlowMap.Start()
}
func (c *Core) StopPolling() {
if TaskFlowMap != nil {
// 停止任务流
TaskFlowMap.Close()
}
}
func (c *Core) HasRunningTask(chnId string) int {
pollingId := fmt.Sprintf("%s_polling", chnId)
if TaskFlowMap.HasRunning(pollingId) {
return 1
}
return 0
}
func (c *Core) AddPollingTask(task *vqd.VqdPolling) error {
chnId := task.ChannelID
pollingId := fmt.Sprintf("%s_polling", chnId)
para := vqdcms.NewVQDPara(TaskFlowMap.TaskTemplate)
info := vqdcms.VQDHandleInfo{
ChannelID: chnId,
ChannelName: task.ChannelName,
TaskID: task.ID,
TaskName: "轮询",
TemplateID: TaskFlowMap.TaskTemplate.ID,
TemplateName: TaskFlowMap.TaskTemplate.Name,
PlanID: 0,
PlanName: "全量",
Plans: "111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
}
newTask := NewPollingTask(pollingId, time.Duration(c.Cfg.VqdConfig.PollingTime)*time.Second, time.Duration(c.Cfg.VqdConfig.PollingTime+1)*time.Second, para, info)
TaskFlowMap.AddTask(newTask)
return nil
}
func (c *Core) UpdatePollingTask() {
cof := c.Cfg.VqdConfig
TaskFlowMap.BatchSetExecuteDuration(time.Duration(cof.PollingTime) * time.Second)
TaskFlowMap.BatchSetTimeout(time.Duration(cof.PollingTime+1) * time.Second)
TaskFlowMap.SetMaxConcurrent(int(cof.PollingNum))
taskTemplate, err := c.VqdTaskCore.GetIDVqdTaskTemplate(context.TODO(), int64(cof.PollingTemplate))
if err != nil {
slog.Error("vqd polling find template", "err", err.Error())
}
if taskTemplate != nil {
TaskFlowMap.SetTemplate(taskTemplate)
}
}
func (c *Core) DelPollingTask(chnId string) error {
pollingId := fmt.Sprintf("%s_polling", chnId)
TaskFlowMap.RemoveTask(pollingId, true)
c.DelPollingTaskVqd(pollingId)
return nil
}
func (c *Core) AddPollingTaskVqd(pollingId string, para vqdcms.VQDPara, info vqdcms.VQDHandleInfo) {
v := vqdcms.NewVQDHandle(c.ResultCb, c.HostCore, info).Create(para, info.Plans)
VqdPollingTaskMap.StoreChildMap(pollingId, v)
}
func (c *Core) DelPollingTaskVqd(pollingId string) {
v, ok := VqdPollingTaskMap.LoadTaskMap(pollingId)
if ok {
v.Destroy()
}
VqdPollingTaskMap.DeleteTaskMap(pollingId)
}
type PollingTask struct {
id string
timeout time.Duration
executeDuration time.Duration
info vqdcms.VQDHandleInfo
para vqdcms.VQDPara
mu sync.RWMutex // 任务配置修改锁
}
func NewPollingTask(id string, executeDuration, timeout time.Duration, para vqdcms.VQDPara, info vqdcms.VQDHandleInfo) *PollingTask {
return &PollingTask{
id: id,
timeout: timeout,
executeDuration: executeDuration,
para: para,
info: info,
}
}
func (d *PollingTask) ID() string { return d.id }
// Timeout 读取超时时间(读锁保护)
func (d *PollingTask) Timeout() time.Duration {
d.mu.RLock()
defer d.mu.RUnlock()
return d.timeout
}
func (d *PollingTask) GetPara() vqdcms.VQDPara {
d.mu.RLock()
defer d.mu.RUnlock()
return d.para
}
func (d *PollingTask) GetHandleInfo() vqdcms.VQDHandleInfo {
d.mu.RLock()
defer d.mu.RUnlock()
return d.info
}
// ExecuteDuration 读取执行时长(读锁保护)
func (d *PollingTask) ExecuteDuration() time.Duration {
d.mu.RLock()
defer d.mu.RUnlock()
return d.executeDuration
}
// SetTimeout 动态修改超时时间(写锁保护)
func (d *PollingTask) SetTimeout(newTimeout time.Duration) {
d.mu.Lock()
oldTimeout := d.timeout
d.timeout = newTimeout
d.mu.Unlock()
slog.Debug("任务超时时间修改", "id", d.id, "old", oldTimeout, "news", newTimeout)
}
// SetExecuteDuration 动态修改执行时长(写锁保护)
func (d *PollingTask) SetExecuteDuration(newDuration time.Duration) {
d.mu.Lock()
oldDuration := d.executeDuration
d.executeDuration = newDuration
d.mu.Unlock()
slog.Debug("任务执行时长修改", "id", d.id, "old", oldDuration, "news", newDuration)
}
func (d *PollingTask) Execute(ctx context.Context) error {
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
// 执行时读取最新的执行时长配置
targetDuration := d.ExecuteDuration()
elapsed := 0 * time.Millisecond
for {
select {
case <-ctx.Done():
return fmt.Errorf("任务被取消: %v (预期执行: %v, 已执行: %v)", ctx.Err(), targetDuration, elapsed)
case <-ticker.C:
elapsed += 100 * time.Millisecond
if elapsed >= targetDuration {
fmt.Println("执行成功:", d.ID())
return nil
}
}
}
}