时间轮

  1. 低精度,因为格子大小是固定的
  2. 如果长时间没有到期任务,这种方案会带来空推进的问题,从而造成一定的性能损耗
  3. 操作和逻辑简单

单轮

  1. 可以使用倍数n来作为时间轮转动的数量

多轮

  1. 小轮一圈,大轮一格
  2. 添加时候先挂大轮,大轮到了再拿下来挂在小轮相对位置

时间堆

  1. 高精度
  2. 逻辑复杂
  3. 一般用四叉堆
  4. 插入和删除的复杂度是logn(因为涉及到排序二分搜索)

思路

  1. 类似二叉堆,最小先执行的时间放在上面
  2. 到了实现执行之后重新计算堆

cron

  • 使用类似二叉堆的办法
	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))// 直接数组排序
		var timer *time.Timer
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			timer = time.NewTimer(100000 * time.Hour)
		} else {
			timer = time.NewTimer(c.entries[0].Next.Sub(now))//计算最小到期时间并且计时
		}

		for {
			select {
			case now = <-timer.C://触发标识出现计时器到期
				now = now.In(c.location)
				// Run every entry whose next time was less than now
				for _, e := range c.entries {
					if e.Next.After(now) || e.Next.IsZero() {
						break
					}
					c.startJob(e.WrappedJob)
					e.Prev = e.Next
					e.Next = e.Schedule.Next(now)//计算下一次触发时间
					c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
				}

			case newEntry := <-c.add://添加之后会重新计算最小到期时间
				timer.Stop()
				now = c.now()
				newEntry.Next = newEntry.Schedule.Next(now)
				c.entries = append(c.entries, newEntry)
				c.logger.Info("added", "now", now, "entry", newEntry.ID, "next", newEntry.Next)

			//...略

			case id := <-c.remove:
				timer.Stop()
				now = c.now()
				c.removeEntry(id)//删除节点
				c.logger.Info("removed", "entry", id)
			}

			break//无论结果到这里都会出来重新计算
		}
	}

Kafka中的方法

  • 两者结合使用.Kafka 中的时间轮(TimingWheel)是一个存储定时任务的环形队列,底层采用数组实现,数组中的每个元素可以存放一个定时任务列表(TimerTaskList)。TimerTaskList是一个环形的双向链表,链表中的每一项表示的都是定时任务项(TimerTaskEntry),其中封装了真正的定时任务(TimerTask)。
  • DelayQueue 中保存着所有的 ==TimerTaskList== (注意这里并不保存task只是相当于把那个时间点有task的信息时间点记录下,这样减少了堆元素的数量)对象,根据时间来排序,这样延时越小的任务排在越前面。
  • 外部通过一个线程(叫做ExpiredOperationReaper)从 DelayQueue 中获取超时的任务列表 TimerTaskList,然后根据 TimerTaskList 的 过期时间来精确推进时间轮的时间,这样就不会存在空推进的问题啦。
  • DelayQueue 只存放了 TimerTaskList,并不是所有的 TimerTask,数量并不多,相比空推进带来的影响是利大于弊的,DelayQueue才是真正对任务进行延时排序的地方,时间轮的用途是分级聚合任务,减少DelayQueue中的对象数。

Crony

  • node和admin都是通过etcd进行交流和沟通
  • node通过定时续租来保证任务运行,admin寻找最少任务数量的节点运行
  • 任务类型支持http和cmd任务
  • 触发通知支持Email
  • 底层为时间堆算法

参考

  • https://zhuanlan.zhihu.com/p/342285267