一个简单的netty服务端
1 | public final class Server { |
- 服务端的socket在哪里初始化?
- 在哪里accept连接?
Netty服务端启动的过程
- 创建服务端Channel
调用JDK的API创建Channel,绑定一些基本组件 - 初始化服务端Channel
初始化一些基本属性,添加一些逻辑处理器 - 注册selector
将底层的Channel注册到事件轮询器selector上,再把服务端channel注册到底层 - 端口绑定
创建服务端Channel
1 | bind()[用户端代码入口] |
最终在channelFactory.newChannel()中通过反射创建Channel。
1 | final ChannelFuture initAndRegister() { |
此时发现这个channelFactory实际是一个叫ReflectiveChannelFactory的类型,是他用反射创建的channel.
那么这个ReflectiveChannelFactory是在哪里初始化的?
看一下上一节的代码中的
1 | .channel(NioServerSocketChannel.class)//设置服务端的channel |
中到底做了哪些事情。
点进去会发现是这么一段
1 | return channelFactory(new ReflectiveChannelFactory<C>(channelClass))//channelClass参数就是NioServerSocketChannel.class |
也就是这个时候帮他设置到里面去的,所以最终反射构 建的channel是NioServerSocketChannel.class。
知道它通过反射把channel构造为NioServerSocketChannel后,去看看这个NioServerSocketChannel的构造函数会做那些事。
反射构建服务端Channel
了解我们通过反射构造Channel的同时做了哪些事.
1 | newSocket()[通过jdk来创建底层jdk channel] |
newSocket()相关部分
以下基于 io.netty.channel.socket.nio.NioServerSocketChannel源代码
NioServerSocketChannel的构造函数调用 newSocket(),里面又调用provider.openServerSocketChannel();创建并返回ServerSocketChannel。ServerSocketChannel是JDK底层的Channel。
这么一来底层的channel就创建好了.接下来构造函数会进入这么一段
1 | public NioServerSocketChannel(ServerSocketChannel channel) {//参数是刚才创建的底层channel |
NioServerSocketChannelConfig()相关部分
NioServerSocketChannel的构造函数中调用了这么一个:
1 | config = new NioServerSocketChannelConfig(this, javaChannel().socket()); |
方便后期对TCP进行配置.
AbstractNioChannel()相关部分
再看它的父类构造器,追踪到AstractNioChannel的构造函数里面会看到
1 | ch.configureBlocking(false); //这里的ch是刚才创建的jdk底层的channel,把它设为非阻塞 |
这么一段.把channel设置为非阻塞流
继续追踪AbstractNioChannel的父类,到了AbstractChannel之后,会看到它的构造函数为
1 | protected AbstractChannel(Channel parent) { |
初始化服务端Channel
再看创建服务端Channel时的过程,他之后将会调用init()方法.
1 | bind()[用户端代码入口] |
那么这个初始化会做哪些事
1 | init()[初始化服务端Channel] |
set ChannelOptions,ChannelAttrs
目的是设置用户自定义的服务端配置.
在io.netty.bootstrap.ServerBootstrap#init中:
1 | void init(Channel channel) throws Exception { |
这里配置的是通过以下的方式配置的,第一节中的代码示例中没有配.
1 | ServerBootstrap b = new ServerBootstrap(); |
set childOption,childAttr
这一步是为了设置用户自定义的连接配置,为每个新连接都配置. 值是用户代码中.childOption()和.childAttr()配置的部分.
同样在io.netty.bootstrap.ServerBootstrap#init中:
1 | synchronized (childOptions) { |
config handler
配置完成后接下来在,在pipeline中把用户自定义的handler放入.这个handler是用户代码中通过.handler()设置的部分.同样是io.netty.bootstrap.ServerBootstrap#init.
1 | ChannelPipeline p = channel.pipeline(); |
config.handler()是如何获取用户设置的handler的?
首先得知道,客户端代码进行.handler(/**/)的时候,调用的是AbstractBootstrap#handler,此时只会单纯把这个handler存到AbstractBootstrap的一个handler属性中.config.handler()就是返回这个handler属性的值.后面的.childHandler()等配置取值都是通过类似方式.
add ServerBootstrapAcceptor
这一步的目的是给accept添加一个NIO的线程,还是接着看io.netty.bootstrap.ServerBootstrap#init.上一步操作后,他会
1 | p.addLast(new ChannelInitializer<Channel>() { |
注册selector
回到io.netty.bootstrap.AbstractBootstrap#initAndRegister方法.构造channel,channel初始化的两步完成后会调用register方法
1 | Channel channel = null; |
步骤:
1 | io.netty.channel.AbstractChannel.AbstractUnsafe#register |
this.eventLoop = eventLoop
实际代码
1 | AbstractChannel.this.eventLoop = eventLoop; |
之后的所有操作都由这个eventLoop处理,之后调用register0()
register0()
io.netty.channel.AbstractChannel.AbstractUnsafe#register0,做了三件事:
1 | doRegister(); //把jdk channel注册到selector |
doRegister()
位置:io.netty.channel.nio.AbstractNioChannel#doRegister
里边调用JDK底层的一个注册方法,把jdk channel注册到selector
1 | selectionKey = javaChannel()//返回之前创建服务端channel时创建的底层Channel |
实际上,注册一个channel的时候是调用jdk底层的一个api去注册到selector中
并把这个this,就是服务端的channel,也一同附加进去,
之后selector轮询到jdk channel时通过获取attachment就能取出netty服务端的Channel,从而进行特定的传播.
invokeHandlerAddedIfNeeded和fireChannelRegistered
由于在用户代码中通过.handler(new ServerHandler())进行了绑定.
invokeHandlerAddedIfNeeded和fireChannelRegistered分别调用用户代码中的com.imooc.netty.ch3.ServerHandler#channelRegistered和com.imooc.netty.ch3.ServerHandler#handlerAdded
但注意,register0()中下面还有个pipeline.fireChannelActive();`:
1 | if (isActive()) { //此时返回false |
但此时由于isActive()返回false,这个fireChannelActive是不会被触发的.
端口绑定
1 | AbstractUnsafe.bind()[入口] |
回到io.netty.bootstrap.AbstractBootstrap#doBind,然后->bind0->..->io.netty.channel.AbstractChannel.AbstractUnsafe#bind.在里面会看到实际绑定本地端口的doBind()和active事件传播的pipline.fireChannelActive().
doBind()
这里实际调用的是io.netty.channel.socket.nio.NioServerSocketChannel#doBind. 通过
1 | javaChannel()//之前创建的jdk底层channel |
对一个端口进行绑定.
pipline.fireChannelActive()
代码相关部分(AbstractUnsafe#bind):
1 | boolean wasActive = isActive();//还没绑定,false |
fireChannelActive,实际上不只是单纯的触发active事件.追踪pipeline.fireChannelActive()->DefaultChannelPipeline#fireChannelActive->AbstractChannelHandlerContext#invokeChannelActive->io.netty.channel.DefaultChannelPipeline.HeadContext#channelActive
这个HeadContext#channelActive的代码如下:
1 |
|
怎样实现重新绑定accept事件?
跟踪这个readIfIsAutoRead()->..->io.netty.channel.AbstractChannelHandlerContext#read->..io.netty.channel.AbstractChannel.AbstractUnsafe#beginRead->..->io.netty.channel.nio.AbstractNioChannel#doBeginRead
doBeginRead的内容如下
1 |
|
回顾一下readInterestOp是从哪来的.在NioServerSocketChannel的构造函数中就带入了
1 | super(null, channel, SelectionKey.OP_ACCEPT); //加入accept事件 |
readIfIsAutoRead()的逻辑完成后,当收到OP_ACCEPT事件的时候就会交给netty处理.