Netty架构篇 - NioServerSocketChannel、NioSocketChannel


前言

在解析Netty中的Channel如何应用之前,首先看下如下的代码:
在这里插入图片描述

这段代码,一些使用过netty的朋友们,都蛮了解的。


再看一段nio的代码。

ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new InetSocketAddress("192.168.0.106", 8080), 1024);
Selector selector = SelectorProvider.provider().openSelector();
channel.register(selector, SelectionKey.OP_ACCEPT);

在下面的源码分析中,你可以看到这段nio代码如何被封装到netty中。

接下来从server.channel(NioServerSocketChannel.class)这行代码开始本篇文章的解析旅程。


服务端Channel的实例化

概述
设置非阻塞模式,建立NioServerSocketChannel与ServerSocket的关联关系,然后通过ReflectiveChannelFactory完成NioServerSocketChannel的实例化。


在这里插入图片描述


首先看下外层的channelFactory(…)方法做了什么事。
在这里插入图片描述
在这里插入图片描述

很显然是设置ChannelFactory属性为一个ReflectiveChannelFactory实例。


然后看下ReflectiveChannelFactory实例如何构造的。
在这里插入图片描述
在这里插入图片描述

由前文可知,方法的参数,我们传递的是NioServerSocketChannel.class。这里利用反射调用它的无参构造器进行实例化。

在这里插入图片描述

后面会分析何时调用这个newChannel()方法。


接下来看下NioServerSocketChannel这个类,声明如下:

在这里插入图片描述

接着看下它的无参构造器。

在这里插入图片描述

先看下newSocket(…)方法的处理逻辑。
在这里插入图片描述

这里的SelectorProvider,通过在ide工具中debug,我们可以知道实际上是KQueueSelectorProvider。(由SelectorProvider.provider()方法生成)

在这里插入图片描述

可以看出得到的是nio原生的ServerSocketChannelImpl这个实例,并且将KQueueSelectorProvider作为参数传入。

回到它的构造器方法。
在这里插入图片描述

  • javaChannel():实际上是我们前面看到的ServerSocketChannelImpl
  • socket():构造一个ServerSocket实例。

由此,NioServerSocketChannelConfigNioServerSocketChannelServerSocket与之关联。如同它的名字一样,存储相关的配置。


接着看调用其父类的构造器做了什么事。
在这里插入图片描述

  • ch:ServerSocketChannelImpl类型。

ch.configureBlocking(false)这行代码,可以看出设置非阻塞模式。同时将readInterestOps属性设置为SelectionKey.OP_ACCEPT,表示监听接收客户端的连接。

接着往下看。
在这里插入图片描述

对parent、id、unsafe、pipeline属性进行赋值。

在后续的AbstractBootstrap#initAndRegister()方法有这样一行代码。
在这里插入图片描述

由此,由ReflectiveChannelFactory完成NioServerSocketChannel的实例化。


服务端Channel的初始化

这部分从代码样例的server.bind(port)这行代码开始分析。

概述
将ServerBoostrap#option(…)、attr(…)等方法设置的属性传递给NioServerSocketChannel。然后在DefaultChannelPipeline中添加ServerBootstrap#handler(…)方法定义的ChannelHandler以及ServerBootstrapAcceptor。


bind(…)方法会走到doBind(…)方法,如下:
在这里插入图片描述

doBind0()方法经过层层方法,会走到NioServerSocketChannel的如下方法中:
在这里插入图片描述

对应nio的ServerSocketChannel#bind(new InetSocketAddress(hostname, port), backlog)方法。


下面重点关注下initAndRegister()方法。(剩余的方法内容会在注册中接着分析)
在这里插入图片描述

  • channelFactory:ReflectiveChannelFactory类型。
  • newChannel():由ReflectiveChannelFactory完成NioServerSocketChannel的实例化。

在这里插入图片描述

在ServerBootstrap#option(…)方法设置的参数传递给NioServerSocketChannelConfig,也就是NioServerSocketChannel的属性。
在ServerBootstrap#attr(AttributeKey, Object)方法设置的key、value传递给NioServerSocketChannel

接着看init(…)剩余的方法。
在这里插入图片描述

在ServerBootstrap#childGroup(…)、childHandler(…)、childOption(…)以及childAttr(…)方法设置的参数传递给ServerBootstrapAcceptor

在这里插入图片描述

在DefaultChannelPipeline中,添加了一个ChannelInitializer。其在initChannel(…)方法,定义了在DefaultChannelPipeline中添加ServerBootstrap#handler(…)方法定义的ChannelHandler、以及ServerBootstrapAcceptor实例。

ChannelInitializer,一个特定的ChannelInboundHandler,提供了便捷的方法(也就是initChannel(…)方法)用于初始化一个Channel。
ServerBootstrapAcceptor,同样是一个ChannelInboundHandler。其封装了如下属性:
在这里插入图片描述


服务端Channel的注册

概述
核心是调用nio原生的ServerSocketChannelImpl#register(Selector sel, int ops, Object att)方法,将Netty的NioServerSocketChannel作为attr参数进行传入,从而与nio原生的ServerSocketChannelImpl建立关联性,最后完成Channel在Selector上的注册。


接着AbstractBootstrap#initAndRegister()方法,展开分析,有如下一行代码:
在这里插入图片描述

  • group():ServerBootstrap#group(…)方法指定的NioEventLoopGroup#bossGroup。

在这里插入图片描述

首先看下next()方法,如下:
在这里插入图片描述

这里是调用父类的next()方法,然后进行类型转换。EventLoop是一个ScheduledExecutorService。

在这里插入图片描述

  • chooser:PowerOfTwoEventExecutorChooser类型。

在这里插入图片描述

  • executors:实际上是NioEventLoop数组。

使用轮询的方式选择一个线程进行channel的注册。


现在,回过头看下register(…)的处理逻辑。

在这里插入图片描述

  • channel:NioServerSocketChannel。
  • this:指代NioEventLoop。

在这里插入图片描述

从register(…)方法中,挑选了如下代码,也就是注册Channel的核心代码。
在这里插入图片描述

接着跟进代码(有省略)。
在这里插入图片描述

  • javaChannel():nio原生的ServerSocketChannelImpl。
  • unwrappedSelector():KQueueSelectorImpl。
  • this:指代NioServerSocketChannel。

可以看出实质上是nio原生方法 AbstractSelectableChannel - register(Selector sel, int ops, Object att)。将ServerSocketChannelImpl注册到Selector上,同时将Netty的NioServerSocketChannel与之关联。


最后我们看下unwrappedSelector()实际的处理。

在这里插入图片描述

在这里插入图片描述

此时的unwrappedSelector是获取SelectorTuple#unwrappedSelector属性。

在这里插入图片描述

对应nio原生的Selector selector = SelectorProvider.provider().openSelector()
实际上底层就是调用KQueueSelectorProvider#openSelector()方法。

在这里插入图片描述


客户端Channel的实例化

在这里插入图片描述

与服务端的不同的是,这里是NioSocketChannel

在这里插入图片描述

这段代码与前面所说的服务端Channel的实例化相同,这里不再分析。


客户端Channel的初始化

bootstrap.connect(host, port)方法开始分析。

在Bootstrap#doResolveAndConnect(…)方法中找到如下代码:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

与我们之前的服务端Channel的初始化相同。这里不再分析。


客户端Channel的注册

接着上面的initAndRegister()方法,找到如下代码:

在这里插入图片描述

同样,还是与服务端Channel的注册完全相同。这里不再分析。