Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用

 

Netty源码分析第一章:Netty启动流程html

 

第四节:注册多路复用java

 

回顾下以上的小节, 咱们知道了channel的的建立和初始化过程, 那么channel是如何注册到selector中的呢?咱们继续分析bootstrap

回到上一小节的代码:promise

final ChannelFuture initAndRegister() { Channel channel = null; try { //建立channel
        channel = channelFactory.newChannel(); //初始化channel
 init(channel); } catch (Throwable t) { //忽略非关键代码
 } //注册channel
    ChannelFuture regFuture = config().group().register(channel); //忽略非关键代码
    return regFuture; }

咱们讲完建立channel和初始化channel的关键步骤, 咱们继续跟注册channel的步骤:安全

ChannelFuture regFuture = config().group().register(channel);

其中, 重点关注下register(channel)这个方法, 这个方法最终会调用到AbstractChannel中内部类AbstractUnsaferegister()方法, 具体如何调用到这个方法, 能够简单带你们捋一下ide

首先看下config()方法, 因为是ServerBootstrap调用的, 因此咱们跟进去:oop

public final ServerBootstrapConfig config() { return config; }

返回的configServerBootrap的成员变量config:源码分析

private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);

 

 

跟到ServerBootstrapConfig的构造方法:this

ServerBootstrapConfig(ServerBootstrap bootstrap) { super(bootstrap); }

继续跟到其父类AbstractBootstrapConfig的构造方法:spa

protected AbstractBootstrapConfig(B bootstrap) { this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap"); }

咱们发现咱们建立的ServerBootstrap做为参数初始化了其成员变量bootstrap

 

回到initAndRegister()方法:

config()返回的是ServerBootstrapConfig对象

再继续跟到group()方法:

public final EventLoopGroup group() { return bootstrap.group(); }

这里调用Bootstrapgroup()方法:

public final EventLoopGroup group() { return group; }

这里返回了AbstractBootstrap的成员变量group, 咱们回顾下第一小节, 还记得AbstractBootstrapgroup(EventLoopGroup group)方法吗?

public B group(EventLoopGroup group) { this.group = group; return (B) this; }

group(EventLoopGroup group)方法初始化了咱们boss线程, group()返回了boss线程, 也就是说 config().group().register(channel) 中的register()方法是boss线程对象调用的, 因为咱们当初初始化的是NioEventLoopGroup, 所以走的是NioEventLoopGroup的父类的MultithreadEventLoopGroupregister()方法

跟到MultithreadEventLoopGroup的register()方法:

public ChannelFuture register(Channel channel) { return next().register(channel); }

这里的代码看起来有点晕, 不要紧, 之后会讲到, 如今能够大概作个了解, NioEventLoopGroup是个线程组, next()方法就是从线程组中选出一个线程, 也就是NioEventLoop线程, 因此这里的next()方法返回的是NioEventLoop对象, 其中register(channel)最终会调用NioEventLoop的父类SingleThreadEventLoopregister(channel)方法

跟到SingleThreadEventLoop的register(channel)方法:

public ChannelFuture register(Channel channel) { return register(new DefaultChannelPromise(channel, this)); }

其中DefaultChannelPromise类咱们以后也会讲到

 

咱们先跟到register(new DefaultChannelPromise(channel, this)):

public ChannelFuture register(final ChannelPromise promise) { ObjectUtil.checkNotNull(promise, "promise"); promise.channel().unsafe().register(this, promise); return promise; }

channel()会返回咱们初始化的NioServerSocketChannel, unsafe()会返回咱们建立channel的时候初始化的unsafe对象

跟进去看AbstractChannelunsafe()的实现:

public Unsafe unsafe() { return unsafe; }

这里返回的unsafe, 就是咱们初始化channel建立的unsafe

回顾下第二小节channel初始化的步骤:

protected AbstractChannel(Channel parent) { this.parent = parent; id = newId(); unsafe = newUnsafe(); pipeline = newChannelPipeline(); }

咱们看unsafe的初始化:unsafe=newUnsafe()

 

跟到newUnsafe(), 咱们以前讲过NioServerSokectChannel的父类是AbstractNioMessageChannel, 因此会调用到到AbstractNioMessageChannel类中的newUnsafe()

跟到AbstractNioMessageChannel类中的newUnsafe():

protected AbstractNioUnsafe newUnsafe() { return new NioMessageUnsafe(); }

咱们看到这里建立了NioMessageUnsafe()对象, 因此在 promise.channel().unsafe().register(this, promise) 代码中, unsafe()是返回的NioMessageUnsafe()对象, 最后调用其父类AbstractUnsafe(也就是AbstractChannel的内部类)register()方法,

 

简单介绍下unsafe接口, unsafe顾名思义就是不安全的, 由于不少对channelio方法都定义在unsafe, 因此netty将其做为内部类进行封装, 防止被外部直接调用, unsafe接口是Channel接口的内部接口, unsafe的子类也分别封装在Channel的子类中, 好比咱们如今剖析的register()方法, 就是封装在AbstractChannel类的内部类AbstractUnsafe中的方法, 有关UnsafeChannel的继承关系以下:

1-4-1

以上内容若是不明白没有关系, 有关NioEventLoop相关会在后面的章节讲到, 目前咱们只是了解是如何走到AbstractUnsafe类的register()便可

 

咱们继续看看register()方法:

public final void register(EventLoop eventLoop, final ChannelPromise promise) { //代码省略 //全部的复制操做, 都交给eventLoop处理(1)
    AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) { register0(promise); } else { try { eventLoop.execute(new Runnable() { @Override public void run() { //作实际主注册(2)
 register0(promise); } }); } catch (Throwable t) { //代码省略
 } } }

咱们跟着注释的步骤继续走, 第一步, 绑定eventLoop线程:

AbstractChannel.this.eventLoop = eventLoop;

eventLoopAbstractChannel的成员变量, 有关eventLoop, 咱们会在绪章节讲到, 这里咱们只须要知道, 每一个channel绑定惟一的eventLoop线程, eventLoop线程和channel的绑定关系就是在这里展示的

 

再看第二步, 作实际注册:

咱们先看if判断, if(eventLoop.inEventLoop())

 

这里是判断是否是eventLoop线程, 显然咱们如今是main()方法所在的线程, 因此走的else, eventLoop.execute()是开启一个eventLoop线程, register0(promise)就是再开启线程以后, 经过eventLoop线程执行的, 这里你们暂时做为了解

 

咱们重点关注register0(promise), 跟进去:

private void register0(ChannelPromise promise) { try { //作实际的注册(1)
 doRegister(); neverRegistered = false; registered = true; //触发事件(2)
 pipeline.invokeHandlerAddedIfNeeded(); safeSetSuccess(promise); //触发注册成功事件(3)
 pipeline.fireChannelRegistered(); if (isActive()) { if (firstRegistration) { //传播active事件(4)
 pipeline.fireChannelActive(); } else if (config().isAutoRead()) { beginRead(); } } } catch (Throwable t) { //省略代码
 } }

咱们重点关注doRegister()这个方法

 

doRegister()最终会调用AbstractNioChanneldoRegister()方法:

protected void doRegister() throws Exception { boolean selected = false; for (;;) { try { //jdk底层的注册方法 //第一个参数为selector, 第二个参数表示不关心任何事件
            selectionKey = javaChannel().register(eventLoop().selector, 0, this); return; } catch (CancelledKeyException e) { //省略代码
 } } }

咱们终于看到和java底层相关的方法了

跟到javaChannel()的方法中:

protected SelectableChannel javaChannel() { return ch; }

这个ch, 就是本章第二小节建立NioServerSocketChannel中初始化的jdk底层ServerSocketChannel

这里register(eventLoop().selector, 0, this)方法中eventLoop().selector, 是得到每个eventLoop绑定的惟一的selector, 0表明此次只是注册, 并不监放任何事件, this是表明将自身(NioEventLoopChannel)做为属性绑定在返回的selectionKey当中, 这个selectionKey就是与每一个channel绑定的jdk底层的SelectionKey对象, 熟悉nio的小伙伴应该不会陌生, 这里再也不赘述

 

回到register0(ChannelPromise promise)方法, 咱们看后续步骤:

步骤(2)是触发handler的须要添加事件, 事件传递的内容咱们将在后续课程详细介绍, 这里没必要深究

步骤(3)是触发注册成功事件(3), 同上

步骤(4)是传播active事件(4), 这里简单强调一下, 这里的方法pipeline.fireChannelActive()第一个注册是执行不到的, 由于isActive()会返回false, 由于链路没完成

本小节梳理了有注册多路复用的相关逻辑, 同窗们能够跟着代码本身走一遍以加深印象

 

上一节: 服务端Channel的初始化

下一节: 绑定端口

相关文章
相关标签/搜索