netty启动过程源码分析,netty连接复用
目录
注册多路复用注册引导的步骤首先看下配置()方法回到initAndRegister()方法:跟到多线程事件循环组的寄存器()方法:回顾下第二小节引导初始化的步骤:我们继续看看寄存器()方法:我们重点关注注册0(承诺),跟进去:我们重点关注多寄存器()这个方法前文传送门:Netty启动流程服务端引导初始化
注册多路复用
回到上一小节的代码:
最终通道未来initandre寄存器(){ Channel Channel=null尝试{ //创建渠道渠道=渠道工厂。新频道();//初始化通道初始化(渠道);} catch (Throwable t) { //忽略非关键代码} //注册ChannelFuture regFuture=config().群组()。注册(渠道);//忽略非关键代码返回regFuture}
注册channel的步骤
我们讲完创建引导和初始化引导的关键步骤,我们继续跟注册引导的步骤:
ChannelFuture regFuture=config().群组()。注册(渠道);其中,重点关注下寄存器(频道)这个方法,这个方法最终会调用到抽象频道中内部类抽象不安全的寄存器()方法,具体如何调用到这个方法,可以简单带大家捋一下
首先看下config()方法
由于是服务器引导调用的,所以我们跟进去:
公共最终服务器bootstrapconfig config(){ return config;}返回的配置是ServerBootrap的成员变量配置:
private final ServerBootstrapConfig config=new ServerBootstrapConfig(this);跟到ServerBootstrapConfig的构造方法:
servebootstrapconfig(服务器引导程序引导程序){ super(引导程序);}继续跟到其父类AbstractBootstrapConfig的构造方法:
protected AbstractBootstrapConfig(B bootstrap){ this。bootstrap=对象实用程序。checknotnull(bootstrap,“bootstrap”);}我们发现我们创建的服务器引导作为参数初始化了其成员变量引导程序
回到initAndRegister()方法:
配置()返回的是ServerBootstrapConfig对象
再继续跟到其群组()方法:
公共最终事件循环组group(){ return bootstrap。group();}这里调用引导程序的群组()方法:
公共最终事件循环组group(){ return group;}这里返回了抽象引导的成员变量组,我们回顾下第一小节,还记得抽象引导的组(事件循环组组)方法吗?
公共B组(EventLoopGroup group){ this。group=组;返回(二)这个;}组(伊芙
ntLoopGroup group)方法初始化了我们boss线程,而group()返回了boss线程,也就是说config().group().register(channel)中的register()方法是boss线程对象调用的,由于我们当初初始化的是NioEventLoopGroup,因此走的是NioEventLoopGroup的父类的MultithreadEventLoopGroup的register()方法
跟到MultithreadEventLoopGroup的register()方法:
public ChannelFuture register(Channel channel) { return next().register(channel);}
这里的代码看起来有点晕,没关系,以后会讲到,现在可以大概做个了解, NioEventLoopGroup是个线程组,而next()方法就是从线程组中选出一个线程,也就是NioEventLoop线程,所以这里的next()方法返回的是NioEventLoop对象,其中register(channel)最终会调用NioEventLoop的父类SingleThreadEventLoop的register(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对象
跟进去看AbstractChannel的unsafe()的实现:
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顾名思义就是不安全的,因为很多对channel的io方法都定义在unsafe中,所以netty将其作为内部类进行封装,防止被外部直接调用, unsafe接口是Channel接口的内部接口, unsafe的子类也分别封装在Channel的子类中,比如我们现在剖析的register()方法,就是封装在AbstractChannel类的内部类AbstractUnsafe中的方法,有关Unsafe和Channel的继承关系如下:
以上内容如果不明白没有关系,有关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;
eventLoop是AbstractChannel的成员变量,有关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()最终会调用AbstractNioChannel的doRegister()方法:
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,因为链路没完成
本小节梳理了有注册多路复用的相关逻辑,同学们可以跟着代码自己走一遍以加深印象
以上就是Netty启动流程注册多路复用源码分析的详细内容,更多关于Netty启动流程的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。