`
zhang011003
  • 浏览: 5124 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

通过走读netty源码来学习netty(一)(基于netty-4.0.19.Final)

 
阅读更多

本文基于netty-4.0.19.Final包

 

本文使用netty事例包中的EchoServer.java类来进行分析

 

先看EchoServer.java中run方法代码

 public void run() throws Exception {
        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .option(ChannelOption.SO_BACKLOG, 100)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(
                             //new LoggingHandler(LogLevel.INFO),
                             new EchoServerHandler());
                 }
             });

            // Start the server.
            ChannelFuture f = b.bind(port).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

 为什么两个group?它们有什么作用?为什么一个handler?为什么又来一个childHandler?
先不管它,直接看重点的bind方法

进入bind方法后,最终会调用到AbstractBootstrap.java中doBind方法,然后调用initAndRegister方法

 final ChannelFuture regFuture = initAndRegister();

 进入initAndRegister方法后,发现先生成了一个新的channel

final Channel channel = channelFactory().newChannel();

 channelFactory如何创建的?

在EchoServer.java中run方法中发现调用了一个channel方法,channel方法中就会调用channelFactory创建

 BootstrapChannelFactory对象的实例,由于是调用的channelFactory().newChannel()方法,我们进入BootstrapChannelFactory类查看newChannel方法,会发现其实是通过反射创建了一个NioServerSocketChannel类的实例

 

  public T newChannel() {
            try {
                return clazz.newInstance();
            } catch (Throwable t) {
                throw new ChannelException("Unable to create Channel from class " + clazz, t);
            }
        }

接着看initAndRegister方法

initAndRegister方法中调用了init方法,查看发现init方法是抽象方法,在ServerBootstrap类中实现

 

       try {
            init(channel);
        } catch (Throwable t) {
            channel.unsafe().closeForcibly();
            return channel.newFailedFuture(t);
        }

 

查看ServerBootstrap类的init方法,发现做了两件事情

1、设置了options和attrs,这两个选项在EchoServer类的run方法中没有调用,先忽略

2、在channel的pipeline中增加了两个ChannelHandler,第一个是EchoServer类的run方法中指定的LoggingHandler,第二个是ChannelInitializer

 

继续看initAndRegister方法

调用group的register方法,而group其实是EchoServer类中run方法中调用group方法设置的NioEventLoopGroup,另外一个为group为childGroup,是定义在ServerBootstrap类中变量,目前还没有用到。

在NioEventLoopGroup方法中查找register方法,发现在父类MultithreadEventLoopGroup中有定义,于是我们跳到了MultithreadEventLoopGroup类的register方法中

 

MultithreadEventLoopGroup中register方法很简单,委派给EventLoop的register方法,而获取EventLoop是通过调用父类的next方法来做到的

 

   @Override
    public EventLoop next() {
        return (EventLoop) super.next();
    }

    @Override
    public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }
 现在我们来到了父类MultithreadEventExecutorGroup的next方法中
    @Override
    public EventExecutor next() {
        return chooser.next();
    }
 chooser是在MultithreadEventExecutorGroup构造方法中实例化的,查看构造方法,发现chooser的构造又和children数组有关系,而children数组长度是由传入的nThreads来决定的
 protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {

        if (threadFactory == null) {
            threadFactory = newDefaultThreadFactory();
        }

        children = new SingleThreadEventExecutor[nThreads];
        if (isPowerOfTwo(children.length)) {
            chooser = new PowerOfTwoEventExecutorChooser();
        } else {
            chooser = new GenericEventExecutorChooser();
        }

        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
                children[i] = newChild(threadFactory, args);
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            }
        }
 那nThreads值是什么呢?threadFactory是否为null呢?args值是什么呢?
由于前面说过,MultithreadEventExecutorGroup类是NioEventLoopGroup类的父类,我们还需要倒回到实例化NioEventLoopGroup类的EchoServer的run方法中找答案
通过在NioEventLoopGroup类中不停地点啊点,发现了所有的答案:
1、如果调用的是NioEventLoopGroup的int参数构造方法(参数值非0),那么nThreads值为传入的值;如果调用的是默认构造方法,那么nThreads值为DEFAULT_EVENT_LOOP_THREADS的值,而DEFAULT_EVENT_LOOP_THREADS值为io.netty.eventLoopThreads属性指定值,如果没有指定io.netty.eventLoopThreads属性,那么DEFAULT_EVENT_LOOP_THREADS值为两倍处理器个数(在MultithreadEventLoopGroup构造方法中给出)
    static {
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
    }
   protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
        super(nThreads == 0? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
    }
 
2、threadFactory值为null(在NioEventLoopGroup构造方法中给出)
    public NioEventLoopGroup(int nThreads) {
        this(nThreads, null);
    }
 
3、args值为SelectorProvider实例(在NioEventLoopGroup构造方法中给出)
    public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
        this(nThreads, threadFactory, SelectorProvider.provider());
    }
 这些问题弄清楚后,终于可以继续往下走了
接下来是判断children长度是否为2的幂,这个方法自认为很简洁,长知识了
    private static boolean isPowerOfTwo(int val) {
        return (val & -val) == val;
    }
 
然后是实例化children数组的每个实例,调用的是newChild方法,找来找去,发现newChild方法是在NioEventLoopGroup中实现的,返回了NioEventLoop的实例
现在知道MultithreadEventLoopGroup中next方法返回的是children数组中的下一个实例,即NioEventLoop实例,而register方法的调用当然也就是委派给NioEventLoop的实例来调用register方法了
这里有一个疑问:children长度是否为2的幂导致调用next方法返回children数组中值的方式有差别,但是有必要这样吗?也许是性能上的考虑吧
 
写了不少了,下次有时间继续走读NioEventLoop的register方法吧
 
 

 

分享到:
评论

相关推荐

    Apache Spark源码走读之5 -- DStream处理的容错性分析

    ### Apache Spark源码走读之五:DStream处理的容错性分析 #### 环境搭建与背景 为了深入理解Apache Spark Streaming中DStream处理的容错机制,本文将从一个简单的Spark Streaming示例出发,逐步分析Spark如何确保...

    Apache Spark源码走读之3 -- Task运行期之函数调用关系分析

    ### Apache Spark源码走读之3 -- Task运行期之函数调用关系分析 #### 概述 Apache Spark作为一款高效的大数据处理框架,在其内部有着复杂的任务调度与执行机制。本文将深入探讨Spark中Task执行期间的具体流程以及...

    Storm源码走读笔记

    本文档是关于Storm源码的详细走读笔记,主要分析了Storm的启动场景、Topology提交过程、worker进程中的线程使用情况、消息传递机制以及 TridentTopology的创建和ack机制等多个方面。 首先,文档提到了Storm集群中的...

    Apache Spark源码走读之4 -- DStream实时流数据处理

    ### Apache Spark源码走读之四:DStream实时流数据处理 #### 一、系统概述与流数据特性 本文档探讨了Apache Spark Streaming的核心概念之一——**DStream**(Discretized Stream)及其如何实现对实时流数据的有效...

    Apache Spark源码走读之2 -- Job的提交与运行

    4. 作业的拆分与调度:Spark的作业调度是基于stage的概念,即根据RDD之间的依赖关系,将作业拆分成多个stage,每个stage包含一组可以并行执行的任务(task)。 5. 任务的提交与执行:任务被提交给集群进行实际的...

    Apache_Spark源码走读

    ### Apache Spark 源码解析概述 #### 一、引言 ...通过对Spark源码的学习,不仅可以深入了解其内部机制,还能更好地利用其功能特性来优化大数据处理应用。希望本文能为您的学习之旅提供有价值的参考。

    Apache Spark源码走读:如何进行代码跟读

    ### Apache Spark源码走读:如何进行代码跟读 #### 概述 本文旨在探讨如何有效地进行Apache Spark源码的阅读与理解。Apache Spark作为一款高性能的分布式计算框架,在大数据处理领域占据着重要地位。其核心由Scala...

    学生走读管理暂行办法.docx

    《学生走读管理暂行办法》是针对在校大学生走读情况而制定的一项管理政策,旨在满足部分学生因特殊原因需要不住校的需求,同时确保学生在外的人身安全和学业不受影响。以下是该办法的主要内容和相关规定: 1. **...

    软件测试期末实验报告-.pdf

    - 代码走读是静态测试的一部分,它涉及到对源代码的审查,而不实际运行程序。在这个程序中,注意到代码结构清晰,包含一个if...else结构,所有的条件分支都被考虑到了。这种分析确保了程序逻辑的完整性,并且所有...

    高二2部 走读申请.docx

    文档标题和描述中并没有涉及到IT行业的具体知识点,其主要内容是一个学生的走读申请,涉及的是教育管理和个人事务管理的范畴。由于标签为空,我们无法从标签中获取额外的IT信息。因此,按照要求,我将不会生成任何与...

    nova-compute源码分析

    ### nova-compute源码分析 #### 一、Nova概述及工作职责 **1.1 Nova的角色与任务** Nova是OpenStack项目中一个至关重要的组成部分,它主要负责虚拟机实例的生命周期管理,包括创建、调度、运行和销毁等功能。具体...

    hadoop源码分析-HDFS部分

    通过源码学习,我们可以掌握Hadoop的内部机制,包括错误检测、数据校验、数据流控制和故障恢复策略等。这对于优化Hadoop集群的性能、提高系统的稳定性以及解决实际生产环境中的问题具有极大的价值。同时,这也为...

    mina源码走读与实例

    ### MINA源码走读与实例 #### 一、MINA概述 **MINA**(**M**ulti **I**nterface **N**etwork **A**pplication)是Apache组织下的一款开源网络通信框架,它主要针对TCP/IP、UDP/IP协议栈提供了高效的封装和扩展能力...

    C++代码走读意见--开发注意事项

    本篇将基于给定的“C++代码走读意见--开发注意事项”文件中的内容,详细探讨其中提及的关键知识点:内存泄漏问题及解决方案。 ##### 内存泄漏问题分析 **问题场景描述**: 在项目开发过程中,由于未妥善处理动态...

    java8源码-java8-source:java8源码走读

    java8 源码 IDEA走读Java源码坏境搭建 新建一个普通java项目(如:java8-source) 创建package(tech.sqlclub.java_source)存放...到此,你可以开始尽情地撸java源码了,记住尽量要多读源码,多读源码,多学习,共勉!

    托福口语翻译词汇.docx

    - **day student**:走读生(原意为“白天的学生”,但在中文中我们通常说“走读生”来表示不在学校住宿的学生) - **in the dark**:一无所知(原意为“在黑暗中”,比喻不知道某些事情) - **cash crops**:经济...

    Arraylist扩容原理走读.png

    Arraylist扩容原理走读

    1走读生管理制度.doc

    走读生管理制度是学校为了维护教学秩序,保障学生安全而制定的一种管理规范,适用于那些不住校...通过这样的管理制度,学校旨在创建一个安全、有序的学习环境,同时强调家庭和学校间的合作,共同促进学生的成长和发展。

    代码走读[总结].pdf

    代码走读是软件开发中的一种重要技术,旨在通过检查代码是否符合编程规范、寻找编译器中的设计陷阱、快速理解源代码、对原有代码的重构等步骤,提高代码的质量和可维护性。 代码走读可以分为四个层次:检查是否符合...

Global site tag (gtag.js) - Google Analytics