Netty 工具类 —— HashedWheelTimer 讲解

一、前言

首先有一篇超时任务的实战分析,文章简短精炼明了,阐述了,为什么要用HashedWheelTimer。

https://chuansongme.com/n/1650380646616

看完后,知道了需求,然后,我们再看它的使用。

 

二、原理

1.概论

(学习一个类,最好的方式是看api文档或源码前的注释,我下载了Netty源码)

这个类用来计划执行非精准的I/O超时。可以通过指定每一格的时间间隔来改变执行时间的精确度。在大多数网络应用中,I/O超时不需要十分准确,因此,默认的时间间隔是100 毫秒,这个值适用于大多数场合。HashedWheelTimer内部结构可以看做是个车轮,简单来说,就是TimerTask的hashTable的车轮。车轮的size默认是512,可以通过构造函数自己设置这个值。注意,当HashedWheelTimer被实例化启动后,会创建一个新的线程,因此,你的项目里应该只创建它的唯一一个实例

(这个类的实现是源自一位教授的论文 Tony Lauck's paper。算法是代码的灵魂,对于有三五年的工程师来说,用代码实现一个算法并非难事,最难的是算法,怪不得google面试的时候全都考的是算法)

2.结构

下方图示阐述了大致的结构模型(自认为画图有所提升,哈哈)

构造器

 1 public HashedWheelTimer(
 2             ThreadFactory threadFactory,    // 用来创建后台线程
 3             long tickDuration,              // 每格时间间隔 默认 100
TimeUnit unit, // 时间单位 默认 毫秒
int ticksPerWheel, // 轮子size(一圈多少格) 默认 512 如果不是2的N次方,则去大于该参数的第一个2的N次方
// 理由 便于Hash值的计算
boolean leakDetection, // 泄露检测 默认 true 4 long maxPendingTimeouts) { // 最大待定超时时间 默认 不设置
27 }

在构造方法里对轮子各个参数进行了设置,接下来看工作线程是哪里启动的。先看下面使用HashedWheelTimer的例子(代码摘自SOFARPC源码)

 1 if (request.isAsync()) {
 2                 TIMEOUT_TIMER.newTimeout(new TimerTask() {
 3                     @Override
 4                     public void run(Timeout timeout) throws Exception {
 5                         Map.Entry<ChannelFuture, AbstractHttpClientHandler> entry = responseChannelHandler
 6                             .removePromise(requestId);
 7                         if (entry != null) {
 8                             ClientHandler handler = entry.getValue();
 9                             Exception e = timeoutException(request, timeoutMills, null);
10                             handler.onException(e);
11                         }
12                     }
13 
14                 }, timeoutMills, TimeUnit.MILLISECONDS);
15             }

然后我们进到newTimeOut方法,主要做了三件事,在start()中启动worker线程,然后计算超时时间,最后创建HashedWheelTimeout,丢给队列,并返回:

 1 @Override
 2     public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
           ...略18 
19         start();       
20 
21         // Add the timeout to the timeout queue which will be processed on the next tick.
22         // During processing all the queued HashedWheelTimeouts will be added to the correct HashedWheelBucket.
23         long deadline = System.nanoTime() + unit.toNanos(delay) - startTime;
           ...29         HashedWheelTimeout timeout = new HashedWheelTimeout(this, task, deadline);
30         timeouts.add(timeout);
31         return timeout;
32     }

我们接着进到worker线程的启动

 1 /**
 2      * Starts the background thread explicitly.  The background thread will
 3      * start automatically on demand even if you did not call this method.
 4      *
 5      * @throws IllegalStateException if this timer has been
 6      *                               {@linkplain #stop() stopped} already
 7      */
 8     public void start() {     // 这个方法为什么是public的?我们可以在实例化HashedWheelTimer后,主动调用这个方法,启动worker线程
 9         switch (WORKER_STATE_UPDATER.get(this)) {
10             case WORKER_STATE_INIT:
11                 if (WORKER_STATE_UPDATER.compareAndSet(this, WORKER_STATE_INIT, WORKER_STATE_STARTED)) {
12                     workerThread.start();            // 在这里启动了worker线程
13                 }
14                 break;
15             case WORKER_STATE_STARTED:
16                 break;
17             case WORKER_STATE_SHUTDOWN:
18                 throw new IllegalStateException("cannot be started once stopped");
19             default:
20                 throw new Error("Invalid WorkerState");
21         }
22 
23         // Wait until the startTime is initialized by the worker.
// 为了等待worker线程初始化startTime
24 while (startTime == 0) { 25 try { 26 startTimeInitialized.await(); 27 } catch (InterruptedException ignore) { 28 // Ignore - it will be ready very soon. 29 } 30 } 31 }

 下一步,我们看看worker线程里面的操作:

首先是初始化startTime,CountDownLatch的触发,

 1         @Override
 2         public void run() {
 3             // Initialize the startTime.
 4             startTime = System.nanoTime();
 5             if (startTime == 0) {
 6                 // We use 0 as an indicator for the uninitialized value here, so make sure it's not 0 when initialized.
 7                 startTime = 1;
 8             }
 9 
10             // Notify the other threads waiting for the initialization at start().
11             startTimeInitialized.countDown();
12 
13             do {
14                 final long deadline = waitForNextTick();
15                 if (deadline > 0) {
16                     int idx = (int) (tick & mask);
17                     processCancelledTasks();
18                     HashedWheelBucket bucket =
19                             wheel[idx];
20                     transferTimeoutsToBuckets();
21                     bucket.expireTimeouts(deadline);
22                     tick++;
23                 }
24             } while (WORKER_STATE_UPDATER.get(HashedWheelTimer.this) == WORKER_STATE_STARTED);
25 
26             // Fill the unprocessedTimeouts so we can return them from stop() method.
27             for (HashedWheelBucket bucket: wheel) {
28                 bucket.clearTimeouts(unprocessedTimeouts);
29             }
30             for (;;) {
31                 HashedWheelTimeout timeout = timeouts.poll();
32                 if (timeout == null) {
33                     break;
34                 }
35                 if (!timeout.isCancelled()) {
36                     unprocessedTimeouts.add(timeout);
37                 }
38             }
39             processCancelledTasks();
40         }

-------------------------------------------------------------

到此为止!上面各个方法里有太多细节的地方,不是很懂,随便乱说,误导大家,不说,光贴代码,感觉好枯燥,索性,写到这里了。。。大家有兴趣,自己接着看源码分析。

。。。。 to be continued!

主要是记录一下学习这个类的一个过程吧。过程中的东西,比结果更加重要!

有空的话,把HashedWheelTimer 算法的论文,打算看看。

 

----------------------------------------------------------------------------------------------

 参考资源:

https://www.jianshu.com/p/328f22432638

转载于:https://www.cnblogs.com/lihao007/p/10588072.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值