功能需求:定时查询库中已确认的行程,如果当前时间距离出发时间已有5小时就修改其状态为已完成;
本示例服务发布后会指定当天20:00第一次执行,如果20点后发布服务那就根据当前时间往后推5分钟。执行业务逻辑代码如果出错,就在设定的时间段中随机加个下次执行的时间。若重复执行成功后那么下次任务开始则是10小时后。详细请看代码:
func ScheduleTask() {
// 首先计算离今天指定时间点最近的下一个时间点
now := time.Now()
//time.Date()创建一个指定日期 20点00分第一次执行
targetTime := time.Date(now.Year(), now.Month(), now.Day(), 20, 0, 0, 0, time.Local)
if targetTime.Before(now) {
// 如果指定时间点晚于当前时间,则计算下一个指定时间点 5分钟后再执行
//targetTime = targetTime.Add(10 * time.Hour)
targetTime = targetTime.Add(5 * time.Minute)
}
fmt.Println("任务将在指定时间执行:", targetTime)
// 根据设定的时间点启动定时器
duration := targetTime.Sub(now)
fmt.Println("距离下次指定时间点的时间间隔为:", duration)
timer := time.NewTimer(duration)
go func() {
for {
<-timer.C // 等待定时器事件
// 这里编写需要执行的定时任务代码
if err := func() error {
defer func() {
if err := recover(); err != nil {
log.E().Msgf("执行定时任务出错:%v", err)
}
}()
fmt.Println("定时任务执行了...")
err := dao.ScheduleTaskCode()
if err != nil {
err = retry(func() error {
return dao.ScheduleTaskCode()
}, 5)
if err != nil {
log.E().Msgf("重复执行定时任务出错,并已达到最大次数:%v", err)
}
}
// 重设定时器,以便下次能够正确触发
targetTime = targetTime.Add(4 * time.Hour)
//targetTime = targetTime.Add(1 * time.Minute)
duration = targetTime.Sub(time.Now())
fmt.Println("任务将在指定时间执行:", targetTime)
fmt.Println("距离下次指定时间点的时间间隔为:", duration)
timer.Reset(duration)
return err
}(); err != nil {
log.E().Msgf("执行定时任务出错:%v", err)
}
}
}()
}
// 需要执行的定时任务代码
func (d *Dao) ScheduleTaskCode() error {
trips := []model.Trip{}
err := d.pg.Where("trip_status = ?", "1").Find(&trips).Error
if err != nil {
return err
}
ids := []int64{}
if len(trips) > 0 {
for _, trip := range trips {
//筛选出发5小时后的行程
after5 := trip.TripTime + 18000
if xtime.Now() >= after5 {
ids = append(ids, trip.ID)
}
}
}
if len(ids) > 0 {
//修改行程状态
err = d.pg.Table("trips").Where("id IN ?", ids).Updates(map[string]interface{}{"trip_status": tripStatus}).Error
if err != nil {
return err
}
//修改同行状态
err = d.pg.Table("trip_applications").Where("trip_id IN ? AND trip_status <> ?", ids, tripApplicationStatus).Updates(map[string]interface{}{"trip_status": tripStatus}).Error
if err != nil {
return err
}
}
return err
}
func retry(fn func() error, times int) error {
min := 5 * time.Second
max := 1 * time.Minute
bo := backoff.New(
backoff.WithMinDuration(min),
backoff.WithMaxDuration(max),
backoff.WithFactor(2))
//执行任务失败,使用Backoff确定下一次任务之前的等待时间
var err error
for i := 0; i < times; i++ {
sleepDuration := bo.Duration()
fmt.Printf("任务执行失败,将在%v后重试,已经执行了%v次。\n", sleepDuration, bo.Attempt())
//睡眠指定时间
time.Sleep(sleepDuration)
if err = fn(); err != nil {
return err
}
time.Sleep(1 * time.Second)
}
//重置Backoff对象
bo.Reset()
return err
}