gitee.com/h79/goutils@v1.22.10/common/scheduler/scheduler_group.go (about) 1 package scheduler 2 3 import ( 4 "gitee.com/h79/goutils/common/logger" 5 "gitee.com/h79/goutils/common/queue" 6 "gitee.com/h79/goutils/common/system" 7 "sync" 8 "time" 9 ) 10 11 var _ queue.IPriority = (*scheduledItem)(nil) 12 13 type scheduledItem struct { 14 Trigger Trigger 15 Job Task 16 nextTime int64 17 } 18 19 // GValue for implement queue.IPriority 20 func (it *scheduledItem) GValue() interface{} { 21 return it 22 } 23 24 // PValue for implement queue.IPriority 25 func (it *scheduledItem) PValue() int64 { 26 return it.nextTime 27 } 28 29 type groupScheduled struct { 30 rm sync.Mutex 31 items *queue.Capacity 32 tm *time.Ticker 33 uniques map[string]*scheduledItem 34 feederTaskCheck system.RunningCheck 35 execTaskCheck system.RunningCheck 36 duration time.Duration 37 minDuration time.Duration 38 execTime time.Duration 39 location *time.Location 40 feeder chan *scheduledItem 41 interrupt chan struct{} 42 index int 43 logEnabled bool 44 } 45 46 const feederCount = 3 47 const defTaskDuration = time.Microsecond * 500 48 49 func newGroupScheduled(idx int, capacity int, location *time.Location, minDuration, duration time.Duration, logEnabled bool) *groupScheduled { 50 if duration < defTaskDuration { 51 duration = defTaskDuration 52 } 53 if minDuration < defTaskDuration { 54 minDuration = defTaskDuration 55 } 56 tm := time.NewTicker(duration) 57 return &groupScheduled{ 58 items: queue.NewCapacity(capacity), 59 feeder: make(chan *scheduledItem, feederCount*2), 60 interrupt: make(chan struct{}, feederCount*10), 61 location: location, 62 duration: duration, 63 minDuration: minDuration, 64 tm: tm, 65 index: idx, 66 logEnabled: logEnabled, 67 uniques: map[string]*scheduledItem{}, 68 } 69 } 70 71 func (gs *groupScheduled) AddTask(task Task, uniqueEnabled bool, generateFn GenerateOrInitFunc) error { 72 return gs.AddTriggerTask(task, NewSimpleTrigger(time.Second*5), uniqueEnabled, generateFn) 73 } 74 75 func (gs *groupScheduled) AddTriggerTask(task Task, trigger Trigger, uniqueEnabled bool, generateFn GenerateOrInitFunc) error { 76 if system.IsQuit() { 77 logger.W("Task", "application is quited,not can add for jobId= %s", task.GetId()) 78 return system.ClosedError 79 } 80 err := gs.add(task, trigger, uniqueEnabled, generateFn) 81 if err == nil { 82 gs.Run() 83 gs.resetTaskTime() 84 } 85 return err 86 } 87 88 func defaultUpdateFn(_ int, _ Task) (Task, Trigger, bool) { 89 return nil, nil, true 90 } 91 92 func (gs *groupScheduled) UpdateTask(jobId string, uniqueEnabled bool, notExistNewFlag bool, generateFn GenerateOrInitFunc) bool { 93 ret := gs.update(jobId, uniqueEnabled, notExistNewFlag, generateFn) 94 if ret && notExistNewFlag { 95 gs.Run() 96 gs.resetTaskTime() 97 } 98 return ret 99 } 100 101 func (gs *groupScheduled) add(task Task, trigger Trigger, uniqueEnabled bool, generateFn GenerateOrInitFunc) error { 102 gs.rm.Lock() 103 defer gs.rm.Unlock() 104 var item *scheduledItem 105 if uniqueEnabled { 106 if it, ok := gs.uniques[task.GetId()]; ok { 107 item = it 108 } 109 if item != nil { 110 if generateFn != nil { 111 generateFn(gs.index, item.Job) 112 } 113 return ErrJobExist 114 } 115 //go to add 116 } 117 nt, err := trigger.NextFireTime(time.Now().In(gs.location).UnixNano()) 118 if err != nil { 119 return err 120 } 121 item = &scheduledItem{Trigger: trigger, Job: task, nextTime: nt} 122 err = gs.items.Add(item) 123 if err != nil { 124 return err 125 } 126 if uniqueEnabled { 127 gs.uniques[task.GetId()] = item 128 } 129 if generateFn != nil { 130 generateFn(gs.index, task) 131 } 132 return nil 133 } 134 135 func (gs *groupScheduled) update(jobId string, uniqueEnabled bool, notExistNewFlag bool, generateFn GenerateOrInitFunc) bool { 136 gs.rm.Lock() 137 defer gs.rm.Unlock() 138 var item *scheduledItem 139 if uniqueEnabled { 140 if it, ok := gs.uniques[jobId]; ok { 141 item = it 142 } 143 } else { 144 item = gs.find(jobId) 145 } 146 if item != nil { 147 if generateFn != nil { 148 _, _, ok := generateFn(gs.index, item.Job) 149 return ok 150 } 151 return true 152 } 153 if notExistNewFlag && generateFn != nil { 154 tk, trigger, _ := generateFn(gs.index, nil) 155 if tk == nil || trigger == nil { 156 return false 157 } 158 nt, err := trigger.NextFireTime(time.Now().In(gs.location).UnixNano()) 159 if err != nil { 160 return false 161 } 162 item = &scheduledItem{Trigger: trigger, Job: tk, nextTime: nt} 163 err = gs.items.Add(item) 164 if err != nil { 165 return false 166 } 167 if uniqueEnabled { 168 gs.uniques[tk.GetId()] = item 169 } 170 _, _, ret := generateFn(gs.index, tk) 171 return ret 172 } 173 return false 174 } 175 176 func (gs *groupScheduled) ForeachTask(update func(group int, task Task, time time.Duration, now, trigger int64) bool) { 177 gs.rm.Lock() 178 defer gs.rm.Unlock() 179 now := time.Now().In(gs.location).UnixNano() 180 gs.items.Foreach(func(v any, index int) bool { 181 if item, ok := v.(*scheduledItem); ok { 182 return update(gs.index, item.Job, gs.execTime, now, item.nextTime) 183 } 184 return false 185 }) 186 } 187 188 func (gs *groupScheduled) StopTask(jobId string) { 189 gs.rm.Lock() 190 defer gs.rm.Unlock() 191 if task := gs.find(jobId); task != nil { 192 task.Job.Cancel() 193 } 194 } 195 196 func (gs *groupScheduled) RemoveTask(jobId string) { 197 gs.rm.Lock() 198 defer gs.rm.Unlock() 199 if task := gs.find(jobId); task != nil { 200 task.Job.Cancel() 201 } 202 } 203 204 func (gs *groupScheduled) HasTask(jobId string) bool { 205 gs.rm.Lock() 206 defer gs.rm.Unlock() 207 return gs.find(jobId) != nil 208 } 209 210 func (gs *groupScheduled) Run() { 211 if system.IsQuit() { 212 return 213 } 214 gs.execTaskCheck.GoRunning(gs.execTask) 215 gs.feederTaskCheck.GoRunning(gs.runFeeder) 216 } 217 218 func (gs *groupScheduled) find(jobId string) *scheduledItem { 219 job, idx := gs.items.Find(func(v interface{}, index int) bool { 220 if item, ok := v.(*scheduledItem); ok { 221 return item.Job.GetId() == jobId 222 } 223 return false 224 }) 225 if idx == -1 { 226 return nil 227 } 228 return job.(*scheduledItem) 229 } 230 231 func (gs *groupScheduled) peek() *scheduledItem { 232 gs.rm.Lock() 233 defer gs.rm.Unlock() 234 var v = gs.items.Peek() 235 item, ok := v.(*scheduledItem) 236 if !ok { 237 return nil 238 } 239 return item 240 } 241 242 func (gs *groupScheduled) calculateNextTime() time.Duration { 243 job := gs.peek() 244 if job != nil { 245 now := time.Now().In(gs.location).UnixNano() 246 if job.nextTime >= now { 247 return time.Duration(job.nextTime - now) 248 } 249 return 0 250 } 251 return gs.duration 252 } 253 254 func (gs *groupScheduled) resetTaskTime() { 255 duration := gs.calculateNextTime() 256 if duration < gs.minDuration { 257 duration = gs.minDuration 258 } 259 if gs.execTime == duration { 260 return 261 } 262 gs.execTime = duration 263 gs.tm.Reset(duration) 264 if gs.logEnabled { 265 logger.N("Schedule", "next run time= %d in group= %d", duration, gs.index) 266 } 267 } 268 269 func (gs *groupScheduled) execTask() { 270 defer gs.tm.Stop() 271 for { 272 gs.resetTaskTime() 273 274 select { 275 case <-system.Closed(): 276 gs.quited() 277 return 278 279 case <-gs.tm.C: 280 for i := 0; i < feederCount-1; i++ { 281 gs.runTask() 282 } 283 284 case <-gs.interrupt: 285 break 286 } 287 } 288 } 289 290 func (gs *groupScheduled) quited() { 291 var opts = With(2) 292 for i := 0; i < len(gs.feeder); i++ { 293 item, ok := <-gs.feeder 294 if !ok { 295 return 296 } 297 _, _ = item.Job.Execute(opts...) 298 } 299 gs.rm.Lock() 300 defer gs.rm.Unlock() 301 for { 302 var v = gs.items.Pop() 303 var item, ok = v.(*scheduledItem) 304 if !ok { 305 break 306 } 307 _, _ = item.Job.Execute(opts...) 308 } 309 } 310 311 func (gs *groupScheduled) addTask(item *scheduledItem) error { 312 gs.rm.Lock() 313 defer gs.rm.Unlock() 314 now := time.Now().In(gs.location).UnixNano() 315 nextTime, err := item.Trigger.NextFireTime(now) 316 if err != nil { 317 return err 318 } 319 if now-item.nextTime > (time.Second * 5).Nanoseconds() { 320 if gs.logEnabled { 321 logger.W("Schedule", "the task is block too long, jobId= '%s' in group=%d", item.Job.GetId(), gs.index) 322 } 323 } 324 item.nextTime = nextTime 325 return gs.items.Add(item) 326 } 327 328 func (gs *groupScheduled) runFeeder() { 329 for { 330 select { 331 case item := <-gs.feeder: 332 err := gs.addTask(item) 333 if err != nil { 334 gs.removeUnique(item.Job.GetId()) 335 logger.E("Schedule", "add task failure for feeder, the task lost, jobId= '%s' in group=%d, err= %s", item.Job.GetId(), gs.index, err) 336 break 337 } 338 gs.interrupt <- struct{}{} 339 340 case <-system.Closed(): 341 return 342 } 343 } 344 } 345 346 func (gs *groupScheduled) pop(now int64) (*scheduledItem, bool) { 347 gs.rm.Lock() 348 defer gs.rm.Unlock() 349 350 var v = gs.items.Peek() 351 var item, ok = v.(*scheduledItem) 352 if !ok { 353 return nil, false 354 } 355 if gs.logEnabled { 356 logger.N("Schedule", "pop the task,jobId= '%s' in group= %d, time= (%d,%d), ex= %v", item.Job.GetId(), gs.index, item.nextTime, now, item.nextTime <= now) 357 } 358 if item.nextTime > now { 359 return nil, false 360 } 361 var it = gs.items.Pop() 362 if it != item { 363 panic("the value not equal") 364 } 365 return item, true 366 } 367 368 func (gs *groupScheduled) runTask() bool { 369 // 没到时间,不用执行 370 now := time.Now().In(gs.location).UnixNano() 371 item, ok := gs.pop(now) 372 if !ok { 373 return false 374 } 375 _, err := item.Job.Execute() 376 if err != nil { 377 gs.removeUnique(item.Job.GetId()) 378 if gs.logEnabled { 379 logger.E("Schedule", "the task execute error, jobId= '%s' in group=%d, err= %s", item.Job.GetId(), gs.index, err) 380 } 381 return true 382 } 383 if item.Job.GetState().IsQuit() { 384 gs.removeUnique(item.Job.GetId()) 385 if gs.logEnabled { 386 logger.E("Schedule", "the task quited, state= '%s', jobId= '%s' in group= %d", item.Job.GetState(), item.Job.GetId(), gs.index) 387 } 388 return true 389 } 390 if system.IsQuit() { 391 gs.removeUnique(item.Job.GetId()) 392 if gs.logEnabled { 393 logger.E("Schedule", "system quited, state= '%s', jobId= '%s' in group= %d", item.Job.GetState(), item.Job.GetId(), gs.index) 394 } 395 return false 396 } 397 item.nextTime = now 398 gs.feeder <- item 399 return true 400 } 401 402 func (gs *groupScheduled) removeUnique(jobId string) { 403 gs.rm.Lock() 404 defer gs.rm.Unlock() 405 delete(gs.uniques, jobId) 406 }