您现在的位置是:亿华云 > 应用开发
Dubbo中的时间轮(Time Wheel)算法应用
亿华云2025-10-09 04:00:59【应用开发】6人已围观
简介1 定时任务Netty、Quartz、Kafka 以及 Linux 都有定时任务功能。JDK 自带的 java.util.Timer 和 DelayedQueue 可实现简单的定时任务,底层用的是堆,
1 定时任务
Netty、间轮Quartz、间轮Kafka 以及 Linux 都有定时任务功能。间轮
JDK 自带的间轮 java.util.Timer 和 DelayedQueue 可实现简单的定时任务,底层用的间轮是堆,存取复杂度都是间轮 O(nlog(n)),但无法支撑海量定时任务。间轮
在任务量大、间轮性能要求高的间轮场景,为了将任务存取及取消操作时间复杂度降为 O(1),间轮会采用时间轮算法。间轮
2 时间轮模型及其应用
一种高效批量管理定时任务的间轮调度模型。一般会实现成一个环形结构,间轮类似一个时钟,间轮分为很多槽,间轮一个槽代表一个时间间隔,每个槽使用双向链表存储定时任务。
指针周期性跳动,跳动到一个槽位,就执行该槽位的定时任务。
Hashed Timing Wheel 结构示意图
计时器维护代价高,如果
处理器在每个时钟滴答声中都会中断 使用精细粒度计时器 未完成的计时器很多需要高效的定时器算法以减少总体中断的开销。云南idc服务商
单层时间轮的容量和精度都是有限的,对于精度要求特别高、时间跨度特别大或是海量定时任务需要调度的场景,通常会使用多级时间轮以及持久化存储与时间轮结合的方案。
模型和性能指标
模型中的规则
客户端调用:
START_TIMER(时间间隔,Request_ID,Expiry_Action) STOP_TIMER(Request_ID)计时器tick调用:
PER_TICK_BOOKKEEPING EXPIRY_PROCESSING性能指标
空间数据结构使用的内存
延迟开始和结束上述任何例程所需的时间
3 Dubbo的时间轮结构
Dubbo 时间轮实现位于 dubbo-common 模块的 org.apache.dubbo.common.timer 包,下面我们就来分析时间轮涉及的核心接口和实现。
核心接口
TimerTask
在 Dubbo 中,所有定时任务都要实现 TimerTask 接口。只定义了一个 run() 方法,入参是一个 Timeout 接口对象。
Timeout
Timeout 对象与 TimerTask 对象一一对应,类似线程池返回的 Future 对象与提交到线程池中的任务对象之间的关系。
通过 Timeout 对象,不仅可以查看定时任务的状态,云服务器提供商还可以操作定时任务(例如取消关联的定时任务)。Timeout 接口中的方法:
Timer 接口定义了定时器的基本行为,核心是 newTimeout() :提交一个定时任务(TimerTask)并返回关联的 Timeout 对象,类似于向线程池提交任务。
HashedWheelTimeout
HashedWheelTimeout 是 Timeout 接口的唯一实现,是 HashedWheelTimer 的内部类。HashedWheelTimeout 扮演了两个角色:
时间轮中双向链表的节点,即定时任务 TimerTask 在 HashedWheelTimer 中的容器
定时任务 TimerTask 提交到 HashedWheelTimer 之后返回的句柄(Handle),用于在时间轮外部查看和控制定时任务
核心字段
prev、next。通过双向链表被用来在HashedWheelTimerBucket链timeouts(定时任务),由于只在WorkerThread上行动,没有必要进行同步/volatile。
task,实际被调度的任务
deadline
,定时任务执行的时间。在创建 HashedWheelTimeout 时指定
计算公式:currentTime(创建 HashedWheelTimeout 的时间) + delay(任务延迟时间) - startTime(HashedWheelTimer 的启动时间),b2b供应网nsstate,定时任务当前所处状态
可选状态:
STATE_UPDATER 用于实现 state 状态变更的原子性。
remainingRounds,当前任务剩余的时钟周期数。时间轮所能表示的时间长度有限,在任务到期时间与当前时刻的时间差,超过时间轮单圈能表示时长,就出现套圈,需要该字段值表示剩余的时钟周期。
核心API
isCancelled()
isExpired()
state()检查当前 HashedWheelTimeout 状态
cancel() 方法
expire()方法
remove()
HashedWheelBucket
时间轮中的一个槽。
时间轮中的槽实际上就是一个用于缓存和管理双向链表的容器,双向链表中的每一个节点就是一个 HashedWheelTimeout 对象,也就关联了一个 TimerTask 定时任务。HashedWheelBucket 持有双向链表的首尾两个节点 - head 和 tail,再加上每个 HashedWheelTimeout 节点均持有前驱和后继引用,即可正、逆向遍历整个链表。
核心API
addTimeout()
pollTimeout()
remove()从双向链表中移除指定的 HashedWheelTimeout 节点。
clearTimeouts()循环调用 pollTimeout() 方法处理整个双向链表,并返回所有未超时或者未被取消的任务。
expireTimeouts()遍历双向链表中的全部 HashedWheelTimeout 节点。在处理到期的定时任务时,会通过 remove() 方法取出,并调用其 expire() 方法执行;对于已取消的任务,通过 remove() 方法取出后直接丢弃;对于未到期的任务,会将 remainingRounds 字段(剩余时钟周期数)减一。
HashedWheelTimer
Timer 接口的实现,通过时间轮算法实现了一个定时器。
职能
根据当前时间轮指针选定对应 HashedWheelBucket 槽,从链表头部开始迭代,计算每个 HashedWheelTimeout 定时任务:
属于当前时钟周期则取出运行 不属于则将其剩余的时钟周期数减一核心域
workerState时间轮当前所处状态,三个可选值,由 AtomicIntegerFieldUpdater 实现其原子地修改。
startTime当前时间轮的启动时间,提交到该时间轮的定时任务的 deadline 字段值均以该时间戳为起点进行计算。
wheel时间轮环形队列,每个元素都是一个槽。当指定时间轮槽数为 n 时,会向上取最靠近 n 的 2 次幂值
timeouts、cancelledTimeoutsHashedWheelTimer 会在处理 HashedWheelBucket 的双向链表前,先处理这俩队列的数据:
timeouts 队列缓冲外部提交时间轮中的定时任务
cancelledTimeouts 队列暂存取消的定时任务
tick位于 HashedWheelTimer$Worker ,时间轮的指针,步长为 1 的单调递增计数器
mask掩码, mask = wheel.length - 1,执行 ticks & mask 便能定位到对应的时钟槽
ticksDuration时间指针每次加 1 所代表的实际时间,单位为纳秒。
pendingTimeouts当前时间轮剩余的定时任务总数。
workerThread时间轮内部真正执行定时任务的线程。
worker真正执行定时任务的逻辑封装这个 Runnable 对象中。
newTimeout()
提交定时任务,在定时任务进入到 timeouts 队列之前会先调用 start() 方法启动时间轮,其中会完成下面两个关键步骤:
确定时间轮的 startTime 字段 启动 workerThread 线程,开始执行 worker 任务。之后根据 startTime 计算该定时任务的 deadline,最后才能将定时任务封装成 HashedWheelTimeout 并添加到 timeouts 队列。
4 时间轮指针一次转动的执行流程
HashedWheelTimer$Worker.run():
时间轮指针转动,时间轮周期开始 清理用户主动取消的定时任务,这些定时任务在用户取消时,记录到 cancelledTimeouts 队列中。在每次指针转动的时候,时间轮都会清理该队列 将缓存在 timeouts 队列中的定时任务转移到时间轮中对应的槽中 根据当前指针定位对应槽,处理该槽位的双向链表中的定时任务 检测时间轮的状态。如果时间轮处于运行状态,则循环执行上述步骤,不断执行定时任务。如果时间轮处于停止状态,则执行下面的步骤获取到未被执行的定时任务并加入 unprocessedTimeouts 队列:遍历时间轮中每个槽位,并调用 clearTimeouts() 方法;对 timeouts 队列中未被加入槽中循环调用 poll() 最后再次清理 cancelledTimeouts 队列中用户主动取消的定时任务。5 定时任务应用
并不直接用于周期性操作,而是只向时间轮提交执行单次的定时任务,在上一次任务执行完成的时候,调用 newTimeout() 方法再次提交当前任务,这样就会在下个周期执行该任务。即使在任务执行过程中出现了 GC、I/O 阻塞等情况,导致任务延迟或卡住,也不会有同样的任务源源不断地提交进来,导致任务堆积。
Dubbo 时间轮应用主要在如下方面:
失败重试, 例如,Provider 向注册中心进行注册失败时的重试操作,或是 Consumer 向注册中心订阅时的失败重试等 周期性定时任务, 例如,定期发送心跳请求,请求超时的处理,或是网络连接断开后的重连机制参考
https://zhuanlan.zhihu.com/p/32906730
很赞哦!(728)
相关文章
- 注册域名要了解几大点?新手有什么方式注册域名?
- 4、club娱乐
- 2、定期提交和投标域名注册。例如,益华网络点击“立即预订”后,平台会抢先为客户注册域名。当然,一个域名可能会被多个客户预订,所以出价最高的人中标。
- 4.域名的整体品牌营销力
- 域名不仅仅是一个简单的网站。对于有长远眼光的公司来说,在运营网站之前确定一个优秀的域名对有长远眼光的公司来说是非常重要的。这对今后的市场营销、产品营销和企业品牌建设都具有十分重要的意义。优秀的域名是企业在市场竞争中获得持久优势的利器。
- 最后提醒我们,域名到期后要及时更新域名,否则可能会丢掉域名,每次抢先注册都不会成功。
- 便宜域名使用如何?小白可以买到便宜域名吗?
- CNAME:对应解析的记录值为域名地址
- 域名资源有限,好域名更是有限,但机会随时都有,这取决于我们能否抓住机会。一般观点认为,国内域名注册太深,建议优先考虑外国注册人。外国注册人相对诚实,但价格差别很大,从几美元到几十美元不等。域名投资者应抓住机遇,尽早注册国外域名。
- 第六:这个圈子里的域名确实是赚钱的一些大玩家,至于小米农,有多少赚钱?几乎没有,也就是说,轿子里只有一个人,而且大多数人都抬着轿子。
热门文章
站长推荐
2016年1月1日:注册价格将降至每年7欧元。
在数以亿计的网站中,我们应该抓住每一个可能带来宣传的机会,域名可以带有企业的名字,一般可以使用汉语拼音或者英语单词或者是相关缩写的形式,只要用户记住了你企业的名字,就能很容易的打出你的网站域名,同样的,记住了网站域名也能很快的记住你公司的名字。
顶级域名可以增加企业品牌的价值。随着经济的快速发展,域名已不再是企业在网络中的独立地位。顶级域名的服务范围、企业产品、综合形象体现等,对于企业单位来说,顶级域名的重要性不言而喻。
4、待所有域名查询结束后可在右侧点击导出结果,即可以excel的文件方式将查询到的结果导出。
.com域名是国际最广泛流行的通用域名,目前全球注册量第一的域名,公司企业注册域名的首选。国际化公司通常会注册该类域名。
小白注册网站域名该怎么办?有什么步骤?
只要我们做的是从目前的市场情况选择域名,从简单易记,从个性特征上,我们就可以找到一个好域名进行注册。域名注册进行域名记录和解析以及绑定网站后,客户可以通过URL登录您的网站。
新手可以注册cc域名吗?cc域名有什么特点?