Netty启动流程注册多路复用源码解析

作者:向南是个万人迷 时间:2021-12-02 16:51:35 

前文传送门:Netty启动流程服务端channel初始化

注册多路复用

回到上一小节的代码:

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的关键步骤, 我们继续跟注册channel的步骤:

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

其中, 重点关注下register(channel)这个方法, 这个方法最终会调用到AbstractChannel中内部类AbstractUnsafe的register()方法, 具体如何调用到这个方法, 可以简单带大家捋一下

首先看下config()方法

由于是ServerBootstrap调用的, 所以我们跟进去:

public final ServerBootstrapConfig config() {
   return config;
}

返回的config是ServerBootrap的成员变量config:

private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);

跟到ServerBootstrapConfig的构造方法:

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

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

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

我们发现我们创建的ServerBootstrap作为参数初始化了其成员变量bootstrap

回到initAndRegister()方法:

config()返回的是ServerBootstrapConfig对象

再继续跟到其group()方法:

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

这里调用Bootstrap的group()方法:

public final EventLoopGroup group() {
   return group;
}

这里返回了AbstractBootstrap的成员变量group, 我们回顾下第一小节, 还记得AbstractBootstrap的group(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的父类的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的继承关系如下:

Netty启动流程注册多路复用源码解析

以上内容如果不明白没有关系, 有关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, 因为链路没完成

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

来源:https://www.cnblogs.com/xiangnan6122/p/10202849.html

标签:Netty,启动流程,注册,多路复用
0
投稿

猜你喜欢

  • Android仿微信语音聊天界面设计

    2023-01-10 01:33:28
  • Java:"失效"的private修饰符

    2021-06-28 19:56:48
  • 利用C#9.0新语法如何提升if语句美感

    2021-06-15 12:48:54
  • 逐步讲解快速排序算法及C#版的实现示例

    2022-09-09 14:36:19
  • Java使用DualPivotQuicksort排序

    2022-05-22 20:58:12
  • Java中线程安全问题

    2021-12-02 05:51:59
  • java启动jar包修改JVM默认内存问题

    2022-03-17 21:50:09
  • c#生成验证码程序

    2023-01-08 18:38:59
  • Android使用SmsManager实现短信发送功能

    2023-08-24 17:54:20
  • C# 绘制统计图大全(柱状图, 折线图, 扇形图)

    2023-03-12 09:16:26
  • Android触摸及手势操作GestureDetector

    2023-07-31 14:13:42
  • C++实现的O(n)复杂度内查找第K大数算法示例

    2023-06-30 15:51:13
  • Android 实现九宫格抽奖功能

    2021-10-02 21:42:27
  • Java设计模式之迭代模式(Iterator模式)介绍

    2022-07-24 16:03:29
  • Android导航栏功能项的显示与屏蔽介绍

    2022-09-27 06:38:55
  • Unity游戏开发之2048游戏的实现

    2023-04-19 00:11:33
  • 使用fastjson中的JSONPath处理json数据的方法

    2021-12-14 09:09:58
  • 基于SpringBoot实现图片上传及图片回显

    2023-01-17 06:26:15
  • Java 阻塞队列和线程池原理分析

    2022-04-19 11:57:45
  • Java monitor机制使用方法解析

    2023-11-09 11:25:56
  • asp之家 软件编程 m.aspxhome.com