// 将 startMs 向下取整到 tickMs 的整数倍(得到 currentTime 之后 startMs 就没用了) private[this] varcurrentTime= startMs - (startMs % tickMs) // rounding down to multiple of tickMs
// overflowWheel can potentially be updated and read by two concurrent threads through add(). // Therefore, it needs to be volatile due to the issue of Double-Checked Locking pattern with JVM // // 由于可能有多个线程同时 update/read 多层 TimingWheel,所以需要加上 volatile 修饰 // // kafka 按需创建 TimingWheel,如果要执行一个很久之后的任务, // 其等待时长超过了本层的 currentTime + interval,就会去创建下一层 TimingWheel。 @volatileprivate[this] var overflowWheel: TimingWheel = null
// Remove the timer task entry if it is already in any other list // We do this outside of the sync block below to avoid deadlocking. // We may retry until timerTaskEntry.list becomes null. // // 如果 timerTaskEntry 已经在其他 TimerTaskList 中, // 需要先解除 timerTaskEntry 和其他 TimerTaskList 的互相引用: // 1. list.remove(entry) // 2. entry.list = null timerTaskEntry.remove()
synchronized { timerTaskEntry.synchronized { if (timerTaskEntry.list == null) {
// 双向链表的前向和后向节点 var next: TimerTaskEntry = null var prev: TimerTaskEntry = null
// if this timerTask is already held by an existing timer task entry, // setTimerTaskEntry will remove it. // // TimerTask 和 TimerTaskEntry 也互相引用 if (timerTask != null) timerTask.setTimerTaskEntry(this)
// 移除(取消)任务,实际就是 // 1. 将当前 entry 从 TimerTaskList 中移除 // 2. 将 entry 中的 list、next、prev 置空 def remove(): Unit = { varcurrentList= list // If remove is called when another thread is moving the entry from a task entry list to another, // this may fail to remove the entry due to the change of value of list. Thus, we retry until the list becomes null. // In a rare case, this thread sees null and exits the loop, but the other thread insert the entry to another list later. while (currentList != null) { currentList.remove(this) currentList = list } } ...... }
// 关联 TimerTaskEntry private[timer] defsetTimerTaskEntry(entry: TimerTaskEntry): Unit = { synchronized { // if this timerTask is already held by an existing timer task entry, // we will remove such an entry first. if (timerTaskEntry != null && timerTaskEntry != entry) timerTaskEntry.remove()
classAdminManager(val config: KafkaConfig, val metrics: Metrics, val metadataCache: MetadataCache, val zkClient: KafkaZkClient) extendsLoggingwithKafkaMetricsGroup{
/* * Advances the clock if there is an expired bucket. If there isn't any expired bucket when called, * waits up to timeoutMs before giving up. * * 如果有过期的 bucket,则推进时钟 * (外层的 Purgatory 会启动一个线程,循环调用这个方法,从而达到每时每刻推进时钟的目标) */ def advanceClock(timeoutMs: Long): Boolean = {
/* background thread expiring operations that have timed out */ // (reaper 是收割的意思)这个线程会循环检查超时操作,并执行具体的任务 privateval expirationReaper = newExpiredOperationReaper()
// 启动超时任务处理线程 if (reaperEnabled) expirationReaper.start()
/** * Check if the operation can be completed, if not watch it based on the given watch keys * * 检查 operation 是否可以完成,如果不行,则根据 watch key 添加到 watcher 中 * * Note that a delayed operation can be watched on multiple keys. It is possible that * an operation is completed after it has been added to the watch list for some, but * not all of the keys. In this case, the operation is considered completed and won't * be added to the watch list of the remaining keys. The expiration reaper thread will * remove this operation from any watcher list in which the operation exists. * * 需要注意的是,一个 delayed operation 可以同时被多个 watcher watch,并且 watch key 不相同。 * 所以有可能出现: 一个 operation 先被添加到一部分 watcher 中,然后 completed 了,但还有一部分 watcher 待添加 * 这种情况下,operation 就不会被添加到剩余的 watcher 中, * expiration reaper thread 将会从所有的 watcher 中移除该 operation。 * * * 这个方法的大致意图是:判断一个延时请求 DelayOperation 能否在当前线程中立刻完成, * a. 如果可以立刻完成,则返回 True * b. 如果不能立刻完成,则将它添加到 TimingWheel 中,并 watch 起来,等待执行的时刻。 * * @param operation the delayed operation to be checked * @param watchKeys keys for bookkeeping the operation * @return true iff the delayed operations can be completed by the caller * 注意,只有 DelayOp 是本线程 tryComplete() 的时候完成的,才会返回 True, * 其他情况都是 False(比如被别的线程 tryComplete()==true 了) */ deftryCompleteElseWatch(operation: T, watchKeys: Seq[Any]): Boolean = { assert(watchKeys.nonEmpty, "The watch key list can't be empty")
// The cost of tryComplete() is typically proportional to the number of keys. Calling tryComplete() for each key is // going to be expensive if there are many keys. Instead, we do the check in the following way through safeTryCompleteOrElse(). // If the operation is not completed, we just add the operation to all keys. Then we call tryComplete() again. At // this time, if the operation is still not completed, we are guaranteed that it won't miss any future triggering // event since the operation is already on the watcher list for all keys. // // tryComplete() 方法的耗时,通常和 keys 的数量成正比。 // 如果 key 很多,那么逐个调用 tryComplete() 的开销会很大,所以,我们用下面的 safeTryCompleteOrElse() 方法来做检查 // 如果 DelayOperation 尚未 completed,我们将 DelayOperation 添加到所有 keys 中,然后再次调用 tryComplete() 方法, // 此时,即便 operation 还是没有 completed,下一次它也一定会触发的。
// 1. 根据 key 从 watchList 中找到 watchers 对象(如果不存在则调用 new Watchers(key) 新建一个) // 2. 调用 watchers.watch(op),把 op 添加到 op list 中 watchKeys.foreach(key => watchForOperation(key, operation))
// 计数器,用于统计当前 过渡区(purgatory) 中 operation 的数量 if (watchKeys.nonEmpty) estimatedTotalOperations.incrementAndGet()
}) returntrue
// if it cannot be completed by now and hence is watched, add to the expire queue also // 因为已经 watch 上了,如果此时没有成功,就把 op 添加到定时任务中 if (!operation.isCompleted) {
/** * Check if some delayed operations can be completed with the given watch key, * and if yes complete them. * * 这个方法,就给外部提供了一个入口, * 可以在任务被插入到定时任务之后,到期之前,还有机会提前完成掉 * * @return the number of completed operations during this process */ defcheckAndComplete(key: Any): Int = { // 根据 key 的 hash 以及 key 自身的值,找到 watcher val wl = watcherList(key) val watchers = inLock(wl.watchersLock) { wl.watchersByKey.get(key) }
val numCompleted = if (watchers == null) 0 else watchers.tryCompleteWatched() debug(s"Request key $key unblocked $numCompleted$purgatoryName operations") numCompleted }
}
然后是超时任务处理线程 ExpiredOperationReaper,它继承自 ShutdownableThread,ShutdownableThread 在 run 方法中循环调用了 doWork(),以达到持续推进时钟的目的:
/** * A background reaper to expire delayed operations that have timed out */ privateclassExpiredOperationReaperextendsShutdownableThread( "ExpirationReaper-%d-%s".format(brokerId, purgatoryName), false) {
override def doWork(): Unit = { advanceClock(200L) } }
abstractclassShutdownableThread(val name: String, val isInterruptible: Boolean = true) extendsThread(name) with Logging { ······ override def run(): Unit = { isStarted = true info("Starting") try { while (isRunning) doWork() } catch { case e: FatalExitError => shutdownInitiated.countDown() shutdownComplete.countDown() info("Stopped") Exit.exit(e.statusCode()) case e: Throwable => if (isRunning) error("Error due to", e) } finally { shutdownComplete.countDown() } info("Stopped") }
// Trigger a purge if the number of completed but still being watched operations is larger than // the purge threshold. That number is computed by the difference btw the estimated total number of // operations and the number of pending delayed operations. // // estimatedTotalOperations: 是当前 Purgatory 中的任务总数 // numDelayed: 是 SystemTimer 中的任务总数,同时也是 TimingWheel 中的任务总数(他们是同一个 AtomicInteger) // // 任务在被加入 Purgatory 之后,在被加入 TimingWheel(SystemTimer) 之前,还会做一次尝试, // 如果任务可以结束,就不会加入 TimingWheel。此时因此这两个值之间有可能会出现差值 // 还有一种情况,就是 TimingWheel 中有很多任务完成了,numDelayed 就会逐渐减少,此时同样会出现差值 // // 如果这个差值超过了清理阈值(purgeInterval),则清理一下 watcher if (estimatedTotalOperations.get - numDelayed > purgeInterval) { // now set estimatedTotalOperations to delayed (the number of pending operations) since we are going to // clean up watchers. Note that, if more operations are completed during the clean up, we may end up with // a little overestimated total number of operations. // // 由于即将清理 watcher(监听器)列表,因此先将 estimatedTotalOperations 设置为当前的未完成操作数(numDelayed) // 需要注意的是,如果在清理过程中有新的操作完成,最终的 estimatedTotalOperations 可能会略高于实际值 estimatedTotalOperations.getAndSet(numDelayed)
// 遍历 watcherLists(监听器列表),对每个 watcher 进行 purgeCompleted() 操作,并统计清理的总数 debug("Begin purging watch lists") val purged = watcherLists.foldLeft(0) { case (sum, watcherList) => sum + watcherList.allWatchers.map(_.purgeCompleted()).sum } debug("Purged %d elements from watch lists.".format(purged)) } }