• gtimer
    • 时间轮设计
    • 默认定时器
    • 性能基准测试
    • 示例1, 间隔任务
    • 示例2, 单例任务

    gtimer

    gtimer是一个并发安全的高性能任务定时器,类似于Java的Timergtimer内部实现采用灵活高效的分层时间轮(Hierarchical Timing Wheel)设计,被设计为可管理维护百万级别以上数量的定时任务。

    gtimer任务定时器与gcron定时任务模块区别:

    • gtimer属于高性能模块,是框架核心模块,构建任何定时任务的基础,任何方法操作耗时均在纳秒级别;
    • gtimer可适用于任何的定时任务场景中,例如: TCP通信、游戏开发等,最小间隔时间粒度为毫秒级别;
    • gcron支持crontab形式的复杂定时任务语法,最小时间间隔为
    • gcron底层实现基于gtimer

    使用场景

    任何定时任务场景,大批量定时任务/延迟任务的场景,超时控制/频率控制的业务场景,对于定时时间准确度要求不高的业务场景。

    注意事项

    1. 任何的定时任务都是有误差的,在时间轮刻度比较大,或者并发量大,负载较高的系统中尤其明显,具体请参考:https://github.com/golang/go/issues/14410
    2. 间隔不会考虑任务的执行时间。例如,如果一项工作需要3分钟才能执行完成,并且计划每隔5分钟运行一次,那么每次任务之间只有2分钟的空闲时间。
    3. 需要注意的是单例模式运行的定时任务,任务的执行时间会影响该任务下一次执行的开始时间。例如:一个每秒执行的任务,运行耗时为1秒,那么在第1秒开始运行后,下一次任务将会在第3秒开始执行。

    使用方式

    1. import "github.com/gogf/gf/g/os/gtimer"

    接口文档

    https://godoc.org/github.com/gogf/gf/g/os/gtimer

    简要说明:

    1. New方法用于创建自定义的任务定时器对象:
      • slot 参数用于指定每个时间轮的槽数;
      • interval 参数用于指定定时器的最小tick时间间隔;
      • level 为非必需参数,用于自定义分层时间轮的层数,默认为6
    2. Add方法用于添加定时任务,其中:
      • interval 参数用于指定方法的执行的时间间隔;
      • job 参数为需要执行的任务方法(方法地址);
    3. AddEntry方法添加定时任务,支持更多参数的控制;
    4. AddSingleton方法用于添加单例定时任务,即同时只能有一个该任务正在运行;
    5. AddOnce方法用于添加只运行一次的定时任务,当运行一次数后该定时任务自动销毁;
    6. AddTimes方法用于添加运行指定次数的定时任务,当运行times次数后该定时任务自动销毁;
    7. Search方法用于根据名称进行定时任务搜索(返回定时任务*Entry对象指针);
    8. Start方法用于启动定时任务(Add后自动启动定时任务);
    9. Stop方法用于停止定时任务;
    10. Close方法用于关闭定时器;

    时间轮设计

    gtimer (任务定时器) - 图1

    gtimer采用的是 分层时间轮(Hierarchical Timing Wheel) 设计,使用的数据结构为array + list,相比较于单层时间轮设计,可以更高效地管理更多的定时任务。分层时间轮的设计类似于时钟的转动,在分层时间轮的内部,仅使用了一个goroutine来“滚动”刻度,有且只有最底层的时间轮会执行刻度的“滚动”。相当于底层的时间轮转一圈,上层的时间轮才会移动一个刻度,如此循环。时间间隔比较大的定时任务将会被添加到高层的时间轮,当刻度指向该任务的槽时,会将该任务移动到低层的时间轮继续运行。

    在时间轮的每一个刻度上绑定了一个并发安全的链表,达到刻度时自动遍历该链表,满足条件的任务将会创建独立的goroutine进行异步执行。不满足条件的任务,或者需要继续执行的任务将会计算剩余的间隔时间,被重新调度到新的时间轮刻度上。

    关于时间轮更详细的介绍,可以查看以下几个链接:

    • http://www.embeddedlinux.org.cn/RTConforEmbSys/5107final/LiB0071.html
    • https://www.slideshare.net/supperniu/timing-wheels

    默认定时器

    使用gtimer的默认定时器时,默认的时间轮槽数为10,间隔时间为50ms,分层大小为6,每一层的时间轮信息如下:

    层级 槽数 间隔 一周大小
    0 10 50ms 500ms
    1 10 500ms 5000ms
    2 10 5000ms 50000ms
    3 10 50000ms 500000ms
    4 10 500000ms 5000000ms
    5 10 5000000ms 50000000ms

    可以使用以下两种方式修改默认的定时器参数:

    1. 使用启动参数
      • gf.gtimer.slots=100: 修改默认的槽数为100
      • gf.gtimer.level=7: 修改默认的分层数为7
      • gf.gtimer.interval=10: 修改默认的时间刻度为10毫秒
    2. 使用环境变量
      • GF_GTIMER_SLOTS=100
      • GF_GTIMER_LEVEL=7
      • GF_GTIMER_INTERVAL=10

    性能基准测试

    1. goos: darwin
    2. goarch: amd64
    3. pkg: github.com/gogf/gf/g/os/gtimer
    4. Benchmark_Add-4 3000000 484 ns/op 152 B/op 5 allocs/op
    5. Benchmark_StartStop-4 100000000 10.1 ns/op 0 B/op 0 allocs/op
    6. PASS
    7. ok command-line-arguments 6.602s

    示例1, 间隔任务

    1. package main
    2. import (
    3. "fmt"
    4. "github.com/gogf/gf/g/os/gtimer"
    5. "time"
    6. )
    7. func main() {
    8. now := time.Now()
    9. interval := 1400*time.Millisecond
    10. gtimer.Add(interval, func() {
    11. fmt.Println(time.Now(), time.Duration(time.Now().UnixNano() - now.UnixNano()))
    12. now = time.Now()
    13. })
    14. select { }
    15. }

    执行后,输出结果为:

    1. 2019-01-17 18:17:37.022442 +0800 CST m=+1.354132542 1.353568s
    2. 2019-01-17 18:17:38.422467 +0800 CST m=+2.754148119 1.399624s
    3. 2019-01-17 18:17:39.82318 +0800 CST m=+4.154851847 1.40066s
    4. 2019-01-17 18:17:41.222422 +0800 CST m=+5.554084408 1.399094s
    5. 2019-01-17 18:17:42.622461 +0800 CST m=+6.954112968 1.399962s
    6. 2019-01-17 18:17:44.022769 +0800 CST m=+8.354411089 1.400251s
    7. ...

    示例2, 单例任务

    1. package main
    2. import (
    3. "github.com/gogf/gf/g/os/glog"
    4. "github.com/gogf/gf/g/os/gtimer"
    5. "time"
    6. )
    7. func main() {
    8. interval := time.Second
    9. gtimer.AddSingleton(interval, func() {
    10. glog.Println("doing")
    11. time.Sleep(5*time.Second)
    12. })
    13. select { }
    14. }

    执行后,输出结果为:

    1. 2019-01-23 17:04:18.892 doing
    2. 2019-01-23 17:04:24.890 doing
    3. 2019-01-23 17:04:29.892 doing
    4. 2019-01-23 17:04:35.891 doing
    5. ...