广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Netty分布式NioEventLoop任务队列执行源码分析
  • 450
分享到

Netty分布式NioEventLoop任务队列执行源码分析

2024-04-02 19:04:59 450人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录执行任务队列跟进runAllTasks方法:我们跟进fetchFromScheduledTaskQueue()方法回到runAllTasks(long timeoutNanos)

前文传送门:NIOEventLoop处理io事件

执行任务队列

继续回到NioEventLoop的run()方法:

protected void run() {
    for (;;) {
        try {
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.CONTINUE:
                    continue;
                case SelectStrategy.SELECT:
                    //轮询io事件(1)
                    select(wakenUp.getAndSet(false));
                    if (wakenUp.get()) {
                        selector.wakeup();
                    }
                default:
            }
            cancelledKeys = 0;
            needsToSelectAgain = false;
            //默认是50
            final int ioRatio = this.ioRatio; 
            if (ioRatio == 100) {
                try {
                    processSelectedKeys();
                } finally {
                    runAllTasks();
                }
            } else {
                //记录下开始时间
                final long iOStartTime = System.nanoTime();
                try {
                    //处理轮询到的key(2)
                    processSelectedKeys();
                } finally {
                    //计算耗时
                    final long ioTime = System.nanoTime() - ioStartTime;
                    //执行task(3)
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            }
        } catch (Throwable t) {
            handleLoopException(t);
        }
        //代码省略
    }
}

我们看到处理完轮询到的key之后, 首先记录下耗时, 然后通过runAllTasks(ioTime * (100 - ioRatio) / ioRatio)执行taskQueue中的任务

我们知道ioRatio默认是50, 所以执行完ioTime * (100 - ioRatio) / ioRatio后, 方法传入的值为ioTime, 也就是processSelectedKeys()的执行时间:

跟进runAllTasks方法:

protected boolean runAllTasks(long timeoutNanos) {
    //定时任务队列中聚合任务
    fetchFromScheduledTaskQueue();
    //从普通taskQ里面拿一个任务
    Runnable task = pollTask();
    //task为空, 则直接返回
    if (task == null) {
        //跑完所有的任务执行收尾的操作
        afterRunningAllTasks();
        return false;
    }
    //如果队列不为空
    //首先算一个截止时间(+50毫秒, 因为执行任务, 不要超过这个时间)
    final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
    long runTasks = 0;
    long lastExecutionTime;
    //执行每一个任务
    for (;;) {
        safeExecute(task);
        //标记当前跑完的任务
        runTasks ++;
        //当跑完64个任务的时候, 会计算一下当前时间
        if ((runTasks & 0x3F) == 0) {
            //定时任务初始化到当前的时间
            lastExecutionTime = ScheduledFutureTask.nanoTime();
            //如果超过截止时间则不执行(nanoTime()是耗时的)
            if (lastExecutionTime >= deadline) {
                break;
            }
        }
        //如果没有超过这个时间, 则继续从普通任务队列拿任务
        task = pollTask();
        //直到没有任务执行
        if (task == null) {
            //记录下最后执行时间
            lastExecutionTime = ScheduledFutureTask.nanoTime();
            break;
        }
    }
    //收尾工作
    afterRunningAllTasks();
    this.lastExecutionTime = lastExecutionTime;
    return true;
}

首先会执行fetchFromScheduledTaskQueue()这个方法, 这个方法的意思是从定时任务队列中聚合任务, 也就是将定时任务中找到可以执行的任务添加到taskQueue中

我们跟进fetchFromScheduledTaskQueue()方法

private boolean fetchFromScheduledTaskQueue() {
    long nanoTime = AbstractScheduledEventExecutor.nanoTime();
    //从定时任务队列中抓取第一个定时任务
    //寻找截止时间为nanoTime的任务
    Runnable scheduledTask  = pollScheduledTask(nanoTime);
    //如果该定时任务队列不为空, 则塞到普通任务队列里面
    while (scheduledTask != null) {
        //如果添加到普通任务队列过程中失败
        if (!taskQueue.offer(scheduledTask)) {
            //则重新添加到定时任务队列中
            scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask);
            return false;
        }
        //继续从定时任务队列中拉取任务
        //方法执行完成之后, 所有符合运行条件的定时任务队列, 都添加到了普通任务队列中
        scheduledTask = pollScheduledTask(nanoTime);
    }
    return true;
}

 long nanoTime = AbstractScheduledEventExecutor.nanoTime()

 代表从定时任务初始化到现在过去了多长时间

 Runnable scheduledTask= pollScheduledTask(nanoTime) 

代表从定时任务队列中拿到小于nanoTime时间的任务, 因为小于初始化到现在的时间, 说明该任务需要执行了

跟到其父类AbstractScheduledEventExecutor的pollScheduledTask(nanoTime)方法中:

protected final Runnable pollScheduledTask(long nanoTime) {
    assert inEventLoop();
    //拿到定时任务队列
    Queue<ScheduledFutureTask<?>> scheduledTaskQueue = this.scheduledTaskQueue;
    //peek()方法拿到第一个任务
    ScheduledFutureTask<?> scheduledTask = scheduledTaskQueue == null ? null : scheduledTaskQueue.peek();
    if (scheduledTask == null) {
        return null;
    }
    if (scheduledTask.deadlineNanos() <= nanoTime) {
        //从队列中删除
        scheduledTaskQueue.remove();
        //返回该任务
        return scheduledTask;
    }
    return null;
}

我们看到首先获得当前类绑定的定时任务队列的成员变量

如果不为空, 则通过scheduledTaskQueue.peek()弹出第一个任务

如果当前任务小于传来的时间, 说明该任务需要执行, 则从定时任务队列中删除

我们继续回到fetchFromScheduledTaskQueue()方法中:

private boolean fetchFromScheduledTaskQueue() {
    long nanoTime = AbstractScheduledEventExecutor.nanoTime();
    //从定时任务队列中抓取第一个定时任务
    //寻找截止时间为nanoTime的任务
    Runnable scheduledTask  = pollScheduledTask(nanoTime);
    //如果该定时任务队列不为空, 则塞到普通任务队列里面
    while (scheduledTask != null) {
        //如果添加到普通任务队列过程中失败
        if (!taskQueue.offer(scheduledTask)) {
            //则重新添加到定时任务队列中
            scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask);
            return false;
        }
        //继续从定时任务队列中拉取任务
        //方法执行完成之后, 所有符合运行条件的定时任务队列, 都添加到了普通任务队列中
        scheduledTask = pollScheduledTask(nanoTime);
    }
    return true;
}

弹出需要执行的定时任务之后, 我们通过taskQueue.offer(scheduledTask)添加到taskQueue中, 如果添加失败, 则通过

scheduledTaskQueue().add((ScheduledFutureTask<?>) scheduledTask)

重新添加到定时任务队列中

如果添加成功, 则通过pollScheduledTask(nanoTime)方法继续添加, 直到没有需要执行的任务

这样就将定时任务队列需要执行的任务添加到了taskQueue中

回到runAllTasks(long timeoutNanos)方法中

protected boolean runAllTasks(long timeoutNanos) {
    //定时任务队列中聚合任务
    fetchFromScheduledTaskQueue();
    //从普通taskQ里面拿一个任务
    Runnable task = pollTask();
    //task为空, 则直接返回
    if (task == null) {
        //跑完所有的任务执行收尾的操作
        afterRunningAllTasks();
        return false;
    }
    //如果队列不为空
    //首先算一个截止时间(+50毫秒, 因为执行任务, 不要超过这个时间)
    final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
    long runTasks = 0;
    long lastExecutionTime;
    //执行每一个任务
    for (;;) {
        safeExecute(task);
        //标记当前跑完的任务
        runTasks ++;
        //当跑完64个任务的时候, 会计算一下当前时间
        if ((runTasks & 0x3F) == 0) {
            //定时任务初始化到当前的时间
            lastExecutionTime = ScheduledFutureTask.nanoTime();
            //如果超过截止时间则不执行(nanoTime()是耗时的)
            if (lastExecutionTime >= deadline) {
                break;
            }
        }
        //如果没有超过这个时间, 则继续从普通任务队列拿任务
        task = pollTask();
        //直到没有任务执行
        if (task == null) {
            //记录下最后执行时间
            lastExecutionTime = ScheduledFutureTask.nanoTime();
            break;
        }
    }
    //收尾工作
    afterRunningAllTasks();
    this.lastExecutionTime = lastExecutionTime;
    return true;
}

首先通过 Runnable task = pollTask() 从taskQueue中拿一个任务

任务不为空, 则通过

final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos 

计算一个截止时间, 任务的执行时间不能超过这个时间

然后在for循环中通过safeExecute(task)执行task

我们跟到safeExecute(task)中:

protected static void safeExecute(Runnable task) {
    try {
        //直接调用run()方法执行
        task.run();
    } catch (Throwable t) {
        //发生异常不终止
        logger.warn("A task raised an exception. Task: {}", task, t);
    }
}

这里直接调用task的run()方法进行执行, 其中发生异常, 只打印一条日志, 代表发生异常不终止, 继续往下执行

回到runAllTasks(long timeoutNanos)方法

protected boolean runAllTasks(long timeoutNanos) {
    //定时任务队列中聚合任务
    fetchFromScheduledTaskQueue();
    //从普通taskQ里面拿一个任务
    Runnable task = pollTask();
    //task为空, 则直接返回
    if (task == null) {
        //跑完所有的任务执行收尾的操作
        afterRunningAllTasks();
        return false;
    }
    //如果队列不为空
    //首先算一个截止时间(+50毫秒, 因为执行任务, 不要超过这个时间)
    final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
    long runTasks = 0;
    long lastExecutionTime;
    //执行每一个任务
    for (;;) {
        safeExecute(task);
        //标记当前跑完的任务
        runTasks ++;
        //当跑完64个任务的时候, 会计算一下当前时间
        if ((runTasks & 0x3F) == 0) {
            //定时任务初始化到当前的时间
            lastExecutionTime = ScheduledFutureTask.nanoTime();
            //如果超过截止时间则不执行(nanoTime()是耗时的)
            if (lastExecutionTime >= deadline) {
                break;
            }
        }
        //如果没有超过这个时间, 则继续从普通任务队列拿任务
        task = pollTask();
        //直到没有任务执行
        if (task == null) {
            //记录下最后执行时间
            lastExecutionTime = ScheduledFutureTask.nanoTime();
            break;
        }
    }
    //收尾工作
    afterRunningAllTasks();
    this.lastExecutionTime = lastExecutionTime;
    return true;
}

每次执行完task, runTasks自增

这里 if ((runTasks & 0x3F) == 0) 代表是否执行了64个任务, 如果执行了64个任务, 则会通过 lastExecutionTime = ScheduledFutureTask.nanoTime() 记录定时任务初始化到现在的时间, 如果这个时间超过了截止时间, 则退出循环

如果没有超过截止时间, 则通过 task = pollTask() 继续弹出任务执行

这里执行64个任务统计一次时间, 而不是每次执行任务都统计, 主要原因是因为获取系统时间是个比较耗时的操作, 这里是Netty的一种优化方式

如果没有task需要执行, 则通过afterRunningAllTasks()做收尾工作, 最后记录下最后的执行时间

以上就是有关执行任务队列的相关逻辑

章节小结

本章学习了有关NioEventLoopGroup的创建, NioEventLoop的创建和启动, 以及多路复用器的轮询处理和task执行的相关逻辑, 通过本章学习, 我们应该掌握如下内容:

        1.  NioEventLoopGroup如何选择分配NioEventLoop

        2.  NioEventLoop如何开启

        3.  NioEventLoop如何进行select操作

        4.  NioEventLoop如何执行task

以上就是Netty分布式NioEventLoop任务队列执行源码分析的详细内容,更多关于Netty分布式NioEventLoop执行任务队列的资料请关注编程网其它相关文章!

--结束END--

本文标题: Netty分布式NioEventLoop任务队列执行源码分析

本文链接: https://www.lsjlt.com/news/143943.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Netty分布式NioEventLoop任务队列执行源码分析
    目录执行任务队列跟进runAllTasks方法:我们跟进fetchFromScheduledTaskQueue()方法回到runAllTasks(long timeoutNanos)...
    99+
    2022-11-13
  • Netty分布式NioEventLoop任务队列执行的方法
    这篇文章主要介绍“Netty分布式NioEventLoop任务队列执行的方法”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Netty分布式NioEventLoop任务队列执行的方法”文章能帮助大家解...
    99+
    2023-06-29
  • Netty源码分析NioEventLoop怎么执行select
    本篇内容主要讲解“Netty源码分析NioEventLoop怎么执行select”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Netty源码分析NioEventLoop怎么执行select”吧!...
    99+
    2023-06-29
  • Netty分布式NioEventLoop优化selector源码解析
    目录优化selectorselector的创建过程代码剖析这里一步创建了这个优化后的数据结构最后返回优化后的selector优化selector selector的创建过程...
    99+
    2022-11-13
  • Netty源码分析NioEventLoop执行select操作入口
    目录select操作的入口NioEventLoop的run方法轮询io事件rebuildSelector()方法分析完了selector的创建和优化的过程, 这一小节分析select...
    99+
    2022-11-13
  • Netty分布式flush方法刷新buffer队列源码分析
    本文小编为大家详细介绍“Netty分布式flush方法刷新buffer队列源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Netty分布式flush方法刷新buffer队列源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入...
    99+
    2023-06-29
  • Netty分布式flush方法刷新buffer队列源码剖析
    目录flush方法这里最终会调用AbstractUnsafe的flush方法跟进addFlush方法回到addFlush方法回到AbstractUnsafe的flush方法我们重点关...
    99+
    2022-11-13
  • Netty分布式编码器写buffer队列逻辑剖析
    目录写buffer队列我们跟到AbstractUnsafe的write方法中回到write方法中我们跟到setUnwritable(invokeLater)方法中前文传送门:抽象编码...
    99+
    2022-11-13
  • Netty分布式行解码器逻辑源码解析
    目录行解码器LineBasedFrameDecoder首先看其参数我们跟到重载的decode方法中我们看findEndOfLine(buffer)方法前文传送门:Netty分布式固定...
    99+
    2022-11-13
  • Netty分布式Server启动流程服务端初始化源码分析
    目录第一节:服务端初始化group方法初始化成员变量初始化客户端Handler第一节:服务端初始化 首先看下在我们用户代码中netty的使用最简单的一个demo: //创建boss和...
    99+
    2022-11-13
  • 如何进行Java任务队列的定义与代码的分析
    如何进行Java任务队列的定义与代码的分析,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Java任务队列需要我们详细的学习,当然我们在不断的使用中还需要注意相关信息的学习。...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作