Hierarchical Time Wheel的golang设计与实现

处理消息时,会有定时消息或延迟消息,需要定时周期性发送和延时发送的需求。借鉴Linux分层时间轮的定时器算法和其他成熟项目的实现,设计了性能强大、高精度的Hierarchical Time Wheel(分层时间轮),基于golang实现。

一、定时的不同设计方案和性能比较

举个例子:滴滴打车订单完成后,如果用户一直不评价,48小时后会将自动评价为5星。

一般来说怎么实现这类“48小时后自动评价为5星”需求呢?

第一种方案:轮询
启动一个cron定时任务,每小时跑一次,将完成时间超过48小时的订单取出,置为5星,并把评价状态置为已评价。

方案的不足:

(1)轮询效率比较低,时间复杂度O(n),执行次数越多越耗时、耗资源

(2)每次扫库,已经被执行过记录,仍然会被扫描(只是不会出现在结果集中),有重复计算的嫌疑

(3)时效性不够好,如果每小时轮询一次,最差的情况下,时间误差会达到1小时

(4)如果通过增加cron轮询频率来减少(3)中的时间误差,(1)中轮询低效和(2)中重复计算的问题会进一步凸显

第二种方案:基于最小堆定时
即把所有定时任务放入最小堆,底层是切片,任务使用list保存。
最小堆是完全二叉树数据结构(左下角),可使用heap作为有序任务队列。最小堆
把任务执行的时间按照完全二叉树,进行从大到小的剩余时间进行排列,每隔一定时间进行for range,找到第一个未到时间的任务即可停止,因为后面的任务都是未到时间的。

具有n个结点的完全二叉树的深度为log2n+1,其时间复杂度等参数如下:
复杂度
随着任务的递增,我们可以看到堆排序的性能会趋向于平稳(对数曲线)
以2为底的对数曲线

方案的不足:
(1)虽然O(nlog2n)大大降低了时间复杂度,但是面对海量定时任务还是会有性能的瓶颈
(2)没有对比,就发现不了不足:
对比图

第三种方案:时间轮
时间轮算法分为:单轮和层级多轮。

单轮:启动一条协程,每秒移动一个刻度,当有定时任务加入时,根据定时的执行周期计算出对应的刻度位置。相当于现实中的钟表,把不同的定时任务安放在不同的刻度位上。当指针经过此刻度的时候立即执行刻度里的任务。
时间轮的设计

多层时间轮:Hierarchical Time Wheel,即Linux内核的实现方案。
多层时间轮
分层的意义在于提高精度和多任务负载均衡。
设计方案:
默认共分为6轮,底层刻度精确到1毫秒
时间轮刻度数长度默认为10,上一层的精度就是 (上一层的精度)1ms* (上一层的总长度)10为10毫秒,长度也为10。如此类推,精度可提升至纳秒级别(1ms以内)。而添加定时任务时,会优先根据定时所需的时间放置在合适的区间,实现多任务负载均衡。

二、多轮定时器的实现

具体代码实现见:https://github.com/liyang-will/timeWheel
核心层级时间轮模块已经实现完毕。需要进一步增加功能接口(如延时队列,循环次数有限制等,后来根据需要会逐渐增加功能接口)。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值