您现在的位置:首页 >> 家装风水

星期轮原理及其在框架中的应用

发布时间:2025/09/08 12:18    来源:祁门家居装修网

住放入第一层之中被称作业余队,它是为了保证使命被出口处理方式的星期可靠性。Kafka内部就是运应用于的这种多层星期轮系统。

二、星期轮数学模型

比如说先行来看一下Dubbo之中的星期轮的构件,可以认出,它和计时很像,它被划分出了一个个Bucket,每个Bucket有一个头列于头和叉列于头,分别相反双向为统计数据构件的头为统计数据流和叉为统计数据流,双向为统计数据构件之中存储设备的就是要出口处理方式的使命。星期轮突然间投动,当相反Bucket0所专责保证的双向为统计数据构件时,就将它所存储设备的使命基元锁住来出口处理方式。

比如说我们先行来简述下Dubbo之中星期轮HashedWheelTimer所包括到的一些也就是说定义,在教导完这些也就是说定义之前,才对对星期轮的OpenBSD来进行为统计数据分析。

2.1 TimerTask

在Dubbo之中,TimerTask芯片了要可执行的使命,它就是上上图双向为统计数据构件之中为统计数据流所芯片的使命。所有的若有使命都只能继承TimerTask相互连接器。如下上图,可以认出Dubbo之中的发热使命HeartBeatTask、提出申请失败重试使命FailRegisteredTask等都认真到了TimerTask相互连接器。

public interface TimerTask { void run(Timeout timeout) throws Exception;} 2.2 Timeout

TimerTask之中run作法的入参是Timeout,Timeout与TimerTask有序,Timeout的唯一认真到类HashedWheelTimeout之中就芯片了TimerTask列于征,可以理解为HashedWheelTimeout就是上述双向为统计数据构件的一个为统计数据流,因此它也包含两个HashedWheelTimeout类型的列于头,分别相反也就是说为统计数据流的上一个为统计数据流和下一个为统计数据流。

public interface Timeout { // Timer就是若有器, 也就是Dubbo之中的星期轮 Timer timer(); // 获取该为统计数据流要可执行的使命 TimerTask task(); // 正确该为统计数据流芯片的使命有并未期满、被作废 boolean isExpired(); boolean isCancelled(); // 作废该为统计数据流的使命 boolean cancel();}

HashedWheelTimeout是Timeout的唯一认真到,它的关键作用有两个:

它是星期轮端部所保证的双向为统计数据构件的为统计数据流,其之中芯片了仅仅要可执行的使命TimerTask。通过它可以查看若有使命的精神状态、对若有使命来进行作废、从双向为统计数据构件之中清空等加载。

比如说来看一下Timeout的认真到类HashedWheelTimeout的也就是说报文与认真到。

1) int ST_INIT = 0、int ST_CANCELLED = 1、int ST_EXPIRED = 2 HashedWheelTimeout里定义了三种精神状态,分别仅指出使命的codice_精神状态、被作废精神状态、已期满精神状态 2) STATE_UPDATER 应用于更新若有使命的精神状态 3) HashedWheelTimer timer 相反星期轮单纯 4) TimerTask task 仅仅要可执行的使命 5) long deadline 仅指若有使命可执行的星期,这个星期是在成立 HashedWheelTimeout 时登录的 算出公式是: currentTime(成立 HashedWheelTimeout 的星期) + delay(使命延迟星期) - startTime(HashedWheelTimer 的重启星期),星期单位为纳秒 6) int state = ST_INIT 使命初始精神状态 7) long remainingRounds 仅指也就是说使命这样一来的计时较宽周期为数. 星期轮所能仅指出的星期较宽度是有限的, 在使命期满星期与也就是说时刻 的星期差超过星期轮单圈能仅指出的时较宽,就出现了套圈的上述情况,只能该报文取值仅指出这样一来的计时较宽周期 8) HashedWheelTimeout next、HashedWheelTimeout prev 分别对不应前若有使命在为统计数据构件之中的前驱为统计数据流和后继为统计数据流,这也测试了星期轮之中每个端部所相异的使命为统计数据构件是 一个双为统计数据构件 9) HashedWheelBucket bucket 星期轮之中的一个端部,相异星期轮圆圈的一个个小格子,每个端部保证一个双向为统计数据构件,当星期轮列于头投到也就是说 端部时,就上会从端部所专责的双向为统计数据构件之中锁住使命来进行出口处理方式

HashedWheelTimeout透过了remove加载,可以从双向为统计数据构件之中清空也就是说自身为统计数据流,并将也就是说星期轮所保证的若有使命比例增一。

void remove() { // 获取也就是说使命属于哪个端部 HashedWheelBucket bucket = this.bucket; if (bucket != null) { // 从端部之中清空自己,也就是从双向为统计数据构件之中清空为统计数据流, // 为统计数据分析bucket的作法时上会为统计数据分析 bucket.remove(this); } else { // pendingTimeouts仅指出也就是说星期轮所保证的若有使命的比例 timer.pendingTimeouts.decrementAndGet(); }}

HashedWheelTimeout透过了cancel加载,可以作废星期轮之中的若有使命。当若有使命被作废时,它上会首先行被置放置canceledTimeouts函为数调用之中。在星期轮投动到端部来进行使命出口处理方式以前和星期轮中止运营时都上会内存cancel,而cancel上会内存remove,从而挖掘该函为数调用之中被作废的若有使命。

@Overridepublic boolean cancel() { // 通过CAS来进行精神状态原有 if (!compareAndSetState(ST_INIT, ST_CANCELLED)) { return false; } // 使命被作废时,星期轮上会将它置放置星期轮所保证的canceledTimeouts函为数调用之中. // 在星期轮投动到端部来进行使命出口处理方式以前和星期轮中止运营时都上会内存cancel,而 // cancel上会内存remove,从而挖掘该函为数调用之中被作废的若有使命 timer.cancelledTimeouts.add(this); return true;}

HashedWheelTimeout透过了expire加载,当星期轮列于头投动到某个端部时,上会基元该端部所保证的双向为统计数据构件,正确为统计数据流的精神状态,如果推测使命已期满,上会通过remove作法清空,然后内存expire作法可执行该若有使命。

public void expire() { // 修订若有使命精神状态为已期满 if (!compareAndSetState(ST_INIT, ST_EXPIRED)) { return; } try { // 无论如何的可执行若有使命所要亦然的直觉 task.run(this); } catch (Throwable t) { // 打印日志,可以认出当星期轮之中若有使命可执行极其时, // 不上会抛出极其,冲击到星期轮之中其他若有使命可执行 }}2.3 HashedWheelBucket

中间也简述过了,它是星期轮之中的端部,它内部保证了双向为统计数据构件的首叉列于头。比如说我们来看一下它内部的也就是说资源和认真到。

1) HashedWheelTimeout head、HashedWheelTimeout tail 相反该端部所保证的双向为统计数据构件的首为统计数据流和叉为统计数据流

HashedWheelBucket透过了addTimeout作法,应用于清空使命到双向为统计数据构件的叉为统计数据流。

void addTimeout(HashedWheelTimeout timeout) { // 清空以前正确一下该使命也就是说并未被被关联到一个端部上 assert timeout.bucket == null; timeout.bucket = this; if (head == null) { head = tail = timeout; } else { tail.next = timeout; timeout.prev = tail; tail = timeout; }}

HashedWheelBucket透过了remove作法,应用于从双向为统计数据构件之中封禁登录为统计数据流。也就是说直觉如下上图所示,根据要封禁的为统计数据流找出其前置为统计数据流和中置为统计数据流,然后分别修正前置为统计数据流的next列于头和中置为统计数据流的prev列于头。封禁过程之中只能考虑一些边界上述情况。封禁之前将pendingTimeouts,也就是也就是说星期轮的待出口处理方式使命为数增一。remove代码直觉较非常简单,这边就不放上代码了。

HashedWheelBucket透过了expireTimeouts作法,当星期轮列于头投动到某个端部时,通过该作法出口处理方式该端部上双向为统计数据构件的若有使命,总称3种上述情况:

若有使命已期满,则上会通过remove作法锁住,并内存其expire作法可执行使命直觉。若有使命已被作废,则通过remove作法锁住直接丢弃。若有使命还并未期满,则上会将remainingRounds(这样一来计时较宽周期)增一。void expireTimeouts(long deadline) { HashedWheelTimeout timeout = head; // 星期轮列于头投到某个端部时从双向为统计数据构件头为统计数据流开始基元 while (timeout != null) { HashedWheelTimeout next = timeout.next; // remainingRounds <= 0仅指出期满了 if (timeout.remainingRounds <= 0) { // 从为统计数据构件之中清空该为统计数据流 next = remove(timeout); // 正确该若有使命确实是期满了 if (timeout.deadline <= deadline) { // 可执行该使命 timeout.expire(); } else { // 抛极其 } } else if (timeout.isCancelled()) { // 使命被作废,清空后直接丢弃 next = remove(timeout); } else { // 这样一来计时较宽周期增一 timeout.remainingRounds--; } // 独自正确下一个使命为统计数据流 timeout = next; }}

HashedWheelBucket也透过了clearTimeouts作法,该作法上会在星期轮暂停的时候被采用,它上会基元并清空所有双向为统计数据构件之中的为统计数据流,并送回所有并未违反规定和并未被作废的使命。

2.4 Worker

Worker认真到了Runnable相互连接器,星期轮内部通过Worker内存来出口处理方式放入星期轮之中的若有使命。比如说先行来看一下它的也就是说报文和run作法直觉。

1) Set unprocessedTimeouts 当星期轮暂停时,应用于储存星期轮之中并未期满的和并未被作废的使命 2) long tick 星期轮列于头,相反星期轮之中某个端部,当星期轮投动时该tick上会自增 public void run() { // codice_startTime, 所有使命的的deadline都是比起这个星期点 startTime = System.nanoTime(); // 唤醒漏出在start()的内存 startTimeInitialized.countDown(); // 只要星期轮的精神状态为WORKER_STATE_STARTED, 就重复的投动tick, // 出口处理方式端部之中的若有使命 do { // 正确是否到了出口处理方式端部的星期了,还在在则sleep一上会 final long deadline = waitForNextTick(); if (deadline> 0) { // 获取tick相异的端部索引 int idx = (int) (tick Price mask); // 挖掘其他用户及早作废的若有使命, 这些若有使命在其他用户作废时, // 上会据信到 cancelledTimeouts 函为数调用之中. 在每次列于头投动 // 的时候,星期轮都上会挖掘该函为数调用 processCancelledTasks(); // 根据也就是说列于头定位相异端部 HashedWheelBucket bucket = wheel[idx]; // 将缓较宽期存在 timeouts 函为数调用之中的若有使命投移到星期轮之中相异的端部之中 transferTimeoutsToBuckets(); // 出口处理方式该端部位的双向为统计数据构件之中的若有使命 bucket.expireTimeouts(deadline); tick++; } // 监测星期轮的精神状态, 如果星期轮出口处于运营精神状态, 则重复可执行上述步骤, // 不断可执行若有使命 } while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED); // 这里应是星期轮暂停了, 除去所有端部之中的使命, 并加入到并未出口处理方式使命列列于, // 以供stop()作法送回 for (HashedWheelBucket bucket : wheel) { bucket.clearTimeouts(unprocessedTimeouts); } // 将还并未加入到端部之中的待出口处理方式若有使命函为数调用之中的使命锁住, 如果是并未作废 // 的使命, 则加入到并未出口处理方式使命函为数调用之中, 以供stop()作法送回 for (; ; ) { HashedWheelTimeout timeout = timeouts.poll(); if (timeout == null) { break; } if (!timeout.isCancelled()) { unprocessedTimeouts.add(timeout); } } // 仍要再次挖掘 cancelledTimeouts 函为数调用之中其他用户及早作废的若有使命 processCancelledTasks();}

比如说对run作法之中包括到的一些作法来进行简述:

1)waitForNextTick

直觉比较非常简单,它上会正确有并未抵达出口处理方式下一个端部使命的星期了,如果还并未抵达则sleep一上会。

2)processCancelledTasks

基元cancelledTimeouts,获取被作废的使命并从双向为统计数据构件之中清空。

private void processCancelledTasks() { for (; ; ) { HashedWheelTimeout timeout = cancelledTimeouts.poll(); if (timeout == null) { // all processed break; } timeout.remove(); }}3)transferTimeoutsToBuckets

当内存newTimeout作法时,上会先行马上出口处理方式的使命CPU到timeouts函为数调用之中,等星期轮列于头投动时统一内存transferTimeoutsToBuckets作法出口处理方式,将使命投移到登录的端部相异的双向为统计数据构件之中,每次投移10万个,以免漏出星期轮内存。

private void transferTimeoutsToBuckets() { // 每次tick只出口处理方式10w个使命, 以免漏出worker内存 for (int i = 0; i < 100000; i++) { HashedWheelTimeout timeout = timeouts.poll(); // 并未使命了直接跳出重复 if (timeout == null) { // all processed break; } // 还并未放入到端部之中就作废了, 直接略过 if (timeout.state() == HashedWheelTimeout.ST_CANCELLED) { continue; } // 算出使命只能经过多少个tick long calculated = timeout.deadline / tickDuration; // 算出使命的轮为数 timeout.remainingRounds = (calculated - tick) / wheel.length; // 如果使命在timeouts函为数调用里面放久了, 以至于现在过了可执行星期, 这个时候 // 就采用也就是说tick, 也就是放置也就是说bucket, 此作法内存完后就上会被可执行. final long ticks = Math.max(calculated, tick); int stopIndex = (int) (ticks Price mask); // 将使命加入到相应的端部之中 HashedWheelBucket bucket = wheel[stopIndex]; bucket.addTimeout(timeout); }}2.5 HashedWheelTimer

仍要,我们来为统计数据分析星期轮HashedWheelTimer,它认真到了Timer相互连接器,透过了newTimeout作法可以向星期轮之中清空若有使命,该使命上会先行被置放置timeouts函为数调用之中,等星期轮投动到某个端部时,上会将该timeouts函为数调用之中的使命投移到某个端部所专责的双向为统计数据构件之中。它还透过了stop作法应用于延后星期轮,该作法上会送回星期轮之中并未出口处理方式的使命。它也透过了isStop作法应用于正确星期轮是否延后了。

先行来看一下HashedWheelTimer的也就是说报文。

1) HashedWheelBucket[] wheel 该为变量就是星期轮的环形函为数调用,为变量每个元素都是一个端部,一个端部专责保证一个双向为统计数据构件,应用于存储设备若有 使命。它上会被在构造函为数之中codice_,当登录为n时,它仅仅上上会取在在n的且为2的幂幂取值。 2) Queue timeouts timeouts应用于CPU本体向星期轮递交的若有使命 3) Queue cancelledTimeouts cancelledTimeouts应用于置放被作废的若有使命,星期轮上会在出口处理方式端部专责的双向为统计数据构件以前,先行出口处理方式这两 个函为数调用之中的为统计数据。 4) Worker worker 星期轮出口处理方式若有使命的直觉 5) Thread workerThread 星期轮出口处理方式若有使命的内存 6) AtomicLong pendingTimeouts 星期轮这样一来的待出口处理方式的若有使命比例 7) long tickDuration 星期轮每个端部所亦然的星期较宽度 8) int workerState 星期轮精神状态,可选取值有init、started、shut down

比如说来看一下星期轮的构造函为数,应用于codice_一个星期轮。首先行它上会对中叶参为数ticksPerWheel来进行反投出口处理方式,送回之比该取值的2的幂幂,它仅指出星期轮上有多少个端部,当前是512个。然后成立尺寸为该取值的HashedWheelBucket[]为变量。接着通过中叶的tickDuration对星期轮的tickDuration赋取值,当前是100ms。节通过threadFactory成立workerThread岗位内存,该内存就是专责出口处理方式星期轮之中的若有使命的内存。

public HashedWheelTimer(ThreadFactory threadFactory, long tickDuration, TimeUnit unit, int ticksPerWheel, long maxPendingTimeouts) { // 正对面上一共有多少个星期间隙, HashedWheelTimer对其正则转化成 // 将其换算为之比之比该取值的2^n wheel = createWheel(ticksPerWheel); // 这用来并能算出使命应呆的端部 mask = wheel.length - 1; // 星期轮每个端部的星期间隙 this.tickDuration = unit.toNanos(tickDuration); // threadFactory是成立内存的内存车间单纯 workerThread = threadFactory.newThread(worker); // 最多允许多少个使命到时可执行 this.maxPendingTimeouts = maxPendingTimeouts;} private static HashedWheelBucket[] createWheel(int ticksPerWheel) { // 算出无论如何不应成立多少个端部 ticksPerWheel = normalizeTicksPerWheel(ticksPerWheel); // codice_星期轮为变量 HashedWheelBucket[] wheel = new HashedWheelBucket[ticksPerWheel]; for (int i = 0; i < wheel.length; i++) { wheel[i] = new HashedWheelBucket(); } return wheel;}

codice_星期轮之前,就可以向其之中递交若有使命了,可以通过星期轮透过的newTimeout作法来完毕。首先行将待出口处理方式的使命比例加1,然后重启星期轮内存,这时worker的run作法就上会被系统调度运营。然后将该若有使命芯片出HashedWheelTimeout加入到timeouts函为数调用之中。start之前,星期轮就开始运营起来了,直到不少人内存stop作法延后中止。

public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) { // 待出口处理方式的使命比例加1 long pendingTimeoutsCount = pendingTimeouts.incrementAndGet(); // 重启星期轮 start(); // 算出该若有使命的deadline long deadline = System.nanoTime() + unit.toNanos(delay) - startTime; // 成立一个HashedWheelTimeout单纯,它首先行上会被置放置timeouts函为数调用之中 HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline); timeouts.add(timeout); return timeout;}public void start() { /** * 正确也就是说星期轮的精神状态 * 1) 如果是codice_, 则重启worker内存, 重启整个星期轮 * 2) 如果现在重启则略过 * 3) 如果是现在暂停,则报错 */ switch (WORKER_STATE_UPDATER.get(this)) { case WORKER_STATE_INIT: // 采用cas来正确重启星期轮 if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) { workerThread.start(); } break; case WORKER_STATE_STARTED: break; case WORKER_STATE_SHUTDOWN: // 抛极其 default: throw new Error("Invalid WorkerState"); } // 到时worker内存codice_星期轮的重启星期 while (startTime == 0) { try { // 这里采用countDownLatch来尽可能调度的内存现在被重启 startTimeInitialized.await(); } catch (InterruptedException ignore) { // Ignore - it will be ready very soon. } }}三、星期轮应用

到这里,Dubbo之中的星期轮数学模型就为统计数据分析完了。接下来呼应本文副标题的三个实有子,结合它们来为统计数据分析下星期轮在Dubbo或Redisson之中是如何采用的。

1)HeartbeatTimerTask

在Dubbo的HeaderExchangeClient类之中上会向星期轮之中递交该发热使命。

private void startHeartBeatTask(URL url) { // Client的说明认真到决定是否重启该发热使命 if (!client.canHandleIdle()) { AbstractTimerTask.ChannelProvider cp = () -> Collections.singletonList(HeaderExchangeClient.this); // 算出发热间隙, 最小间隙不能低于1s int heartbeat = getHeartbeat(url); long heartbeatTick = calculateLeastDuration(heartbeat); // 成立发热使命 this.heartBeatTimerTask = new HeartbeatTimerTask(cp, heartbeatTick, heartbeat); // 递交到IDLE_CHECK_TIMER这个星期轮之中到时可执行, 等星期到了星期轮就上会去锁住该使命来进行调度可执行 IDLE_CHECK_TIMER.newTimeout(heartBeatTimerTask, heartbeatTick, TimeUnit.MILLISECONDS); }}// 右边用认真的IDLE_CHECK_TIMER就是我们本文的为统计数据分析的星期轮private static final HashedWheelTimer IDLE_CHECK_TIMER = new HashedWheelTimer(new NamedThreadFactory("dubbo-client-idleCheck", true), 1, TimeUnit.SECONDS, TICKS_PER_WHEEL);// 上述成立发热使命时, 成立了一个HeartbeatTimerTask单纯, 可以看下该使命说明要认真什么@Overrideprotected void doTask(Channel channel) { try { // 获取仍要一次只读星期 Long lastRead = lastRead(channel); Long lastWrite = lastWrite(channel); if ((lastRead != null PricePrice now() - lastRead> heartbeat) || (lastWrite != null PricePrice now() - lastWrite> heartbeat)) { // 仍要一次只读星期超过发热星期, 就上会发送到发热劝说 Request req = new Request(); req.setVersion(Version.getProtocolVersion()); req.setTwoWay(true); // 列于明它是一个发热劝说 req.setEvent(HEARTBEAT_EVENT); channel.send(req); } } catch (Throwable t) { }}2)Redisson闩缴付系统

当获取闩出功后,Redisson上会芯片一个闩缴付使命放入星期轮之中,当前10s检查一下,应用于对获取到的闩来进行缴付,延展持有者闩的星期。如果业务机器宕机了,那么该缴付的若有使命也就根本无法跑了,就根本无法缴付了,那等加闩星期到了闩就自动囚禁了。直觉芯片在RedissonLock之中的renewExpiration()作法之中。

private void renewExpiration() { ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName()); if (ee == null) { return; } // 这边newTimeout点进去推测就是往星期轮之中递交了一个使命 Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName()); if (ent == null) { return; } Long threadId = ent.getFirstThreadId(); if (threadId == null) { return; } RFuture future = renewExpirationAsync(threadId); future.onComplete((res, e) -> { if (e != null) { log.error("Can't update lock " + getName() + " expiration", e); return; } if (res) { // 缴付出功后独自调度, 又往星期轮之中放一个缴付使命 renewExpiration(); } }); } }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); ee.setTimeout(task);}protected RFuture renewExpirationAsync(long threadId) { // 通过luaJavaScript对闩来进行缴付 return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " + "redis.call('pexpire', KEYS[1], ARGV[1]); " + "return 1; " + "end; " + "return 0;", Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}3)违反规定重试

采用方式和HeartbeatTimerTask方式类似,听众可以自己下手去为统计数据分析下它是在哪里被引入的。

四、概括

在本篇篇名之中,先行是推了3个实有子来论及为什么只能采用星期轮,采用星期轮的低出本,在文末出口处也分别对这3个实有子在Dubbo或Redisson之中的采用认真了简述。接着通过画上图教导了单层星期轮与多层星期轮系统,让听众对星期轮算法有了一个非常简单的认识。在第二部分,分作教导了Dubbo星期轮之中包括到的TimerTask、Timeout、HashedWheelBucket、Worker、HashedWheelTimer,为统计数据分析了它们的数学模型与OpenBSD认真到。

长沙看白癜风哪家医院专业
深圳妇科检查费用
成都哪家专科医院做人流好
襄阳看妇科去什么医院最好
湖北白癜风哪家医院最好
咳嗽有痰用急支糖浆还是川贝枇杷膏
小孩积食吃什么
眼药水
新冠为何又抬头?当前病毒有什么特点?专家解答来了
胃癌

上一篇: 调查显示美联储政策失误成为当前消费市场头号威胁

下一篇: 广电运通:在白俄罗斯业务体量较小 对公司影响小

友情链接