Netty Server在启动时做了什么事情?
new NioServerSocketChannel() 做了什么事?
1. 通过java.nio.channels.spi.SelectorProvider.provider().openServerSocketChannel()生成了一个nio的ServerSocketChannel
2. 创建一个DefaultChannelPipeline放入NioServerSocketChannel对象中(pipeline对象默认存在head & tail节点)
3. 将创建出来的ServerSocketChannel对象保存到NioServerSocketChannel对象中
4. 将NioServerSocketChannel感兴趣的事件(SelectionKey.OP_ACCEPT)保存到NioServerSocketChannel对象中
5. 设置ServerSocketChannel对象为非阻塞模式
netty server的ServerBootstrap在调用bind(int port)时,做了什么事?
1. 将port封装成一个InetSocketAddress对象
2. initAndRegister()
1) channelFactory.newChannel():使用反射调用NioServerSocketChannel的空参构造器构造一个NioServerSocketChannel对象
2) init(channel):初始化NioServerSocketChannel
-> setChannelOptions(channel, options, logger):将配置的options设置到NioServerSocketChannel对象中
-> 将配置的attrs设置到NioServerSocketChannel对象中
-> channel.pipeline().addLast():给NioServerSocketChannel的pipeline中,添加一个处理器,这个处理器是Channel的初始化器(ChannelInitializer)
---> 此时NioServerSocketChannel的pipeline变成了这样【head <==> ChannelInitializer <==> tail】(当channel注册时,会调用initChannel(ctx)方法,会将NioServerSocketChannel的pipeline变成【head <==> ServerBootstrapAcceptor <==> tail】)
3) config().group().register(channel):从bossGroup里拿一个线程来处理channel的注册,将其注册到线程自己的selector上
-> next().register(channel) 在bossGroup中选择一个NioEventLoop,注册channel
-> eventLoop.execute(() -> register0(promise))
--> addTask(() -> register0(promise))
--> 如果是第一次调用eventLoop.execute,则执行以下逻辑
--> startThread() --> doStartThread()
---> SingleThreadEventExecutor.this.run()这个其实就是NioEventLoop.run()
---> NioEventLoop.run() 是一个死循环,每次循环调用 select(wakenUp.getAndSet)false) -> processSelectedKeys() -> runAllTasks()
----> select(wakenUp.getAndSet(false)):死循环监听IO事件,调用selector.select(timeoutMillis),当超时或者有事件发生就会跳出循环执行下面的逻辑
----> processSelectedKeys():处理事件
----> runAllTasks():执行taskQueue里的任务
3. doBind0()
这个方法主要是提交了一个task到nioEventLoop的taskQueue中,提交的task如下
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
就是调用NioServerSocketChannel的绑定接口,最终会调用javaChannel的bind接口,为javaChannel绑定端口。
netty server 启动注册了4个Task,这4个Task都干了什么事情?
1. register0(promise);
注册任务代码:
// io.netty.channel.AbstractChannel.AbstractUnsafe#register
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
这段代码是在 bind(int port) 方法中 initAndRegister() 时调用的。
register0(promise)方法对NioServerSocketChannel启动时来说,重要的逻辑有:
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
}
boolean firstRegistration = neverRegistered;
// 将NioServerSocketChannel注册到selector上,
// 但是这时候注册的是0没有真正的注册accept事件,
// 在pipeline.fireChannelActive()才注册真正的accept事件
doRegister();
neverRegistered = false;
registered = true;
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
} catch (Throwable t) {
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
(1)doRegister():将NioServerSocketChannel注册到selector上,但是这时候注册的是0没有真正的注册accept事件,在pipeline.fireChannelActive()才注册真正的accept事件:
io.netty.channel.AbstractChannel#doRegister
--> javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
(2)pipeline.invokeHandlerAddedIfNeeded():判断是否是第一次注册,如果是第一次注册,执行注册前添加的ChannelHandlers,完成注册
pipeline.invokeHandlerAddedIfNeeded();
--> callHandlerAddedForAllHandlers():拿到完成注册前添加的所有ChannelHandlers:pendingHandlerCallbackHead
--> task.execute():调用PendingHandlerAddedTask的execute()方法
----> callHandlerAdded0(ctx)
------> ctx.callHandlerAdded()
--------> handler().handlerAdded(this):此时会调用ChannelInitializer的handlerAdded()方法,这个方法是ServerBootstrap在init(channel)是添加的
----------> initChannel(ctx):最终调用ChannelInitializer的initChannel(Channel ch)方法,此时ch还是NioServerSocketChannel
----------> removeState(ctx):删除NioServerSocketChannel的pipeline中的ChannelInitializer(一个Handler)
注意:这里的initChannel(ctx)方法会注册一个任务,后续有说,所以执行完这段逻辑之后的pipeline是这样的。
原始pipeline:HeadContext <--> ChannelInitializer <--> TailContext
执行完initChannel后的pipeline:HeadContext <--> TailContext
后续才为这个pipeline添加ServerBootstrapAcceptor(Handler)
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
(3)pipeline.fireChannelRegistered():触发channel的注册事件,链式调用pipeline中的所有handler的channelRegister方法
注意:这里由于是NioServerSocketChannel的注册,所以并不会调用这段逻辑,因为这个时候NioServerSocketChannel还没有绑定端口
if (isActive()) {
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
beginRead();
}
}
// io.netty.channel.socket.nio.NioServerSocketChannel#isActive
// 这个是NioServerSocketChannel的isActive方法,因为这里Netty设计的是先注册再绑定端口号,所以javaChannel().socket().isBound() = false
public boolean isActive() {
return isOpen() && javaChannel().socket().isBound();
}
// io.netty.channel.socket.nio.NioSocketChannel#isActive
// 这个是NioSocketChannel的isActive方法,由于在NioSocketChannel在accept之后就已经算连接了,所以这里isActive方法返回的是true,即如果是NioSocketChannel就会走上面的逻辑
public boolean isActive() {
SocketChannel ch = javaChannel();
return ch.isOpen() && ch.isConnected();
}
2. pipeline.addLast(new ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
注册任务代码:
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
这段代码是在ServerBoostrap的 bind(int port) 中在 init(channel) 时调用的一段代码,往channel添加了一个ChannelInitializer,在register的时候,会通过pipeline.invokeHandlerAddedIfNeeded()调用到该ChannelInitializer的 initChannel(final Channel ch) 方法,然后就会向eventLoop提交一个task
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
这个task主要就是给NioServerSocketChannel的pipeline添加一个ServerBootstrapAcceptor,此时NioServerSocketChannel的pipeline如下:
原始:HeadContext <--> TailContext
添加后:HeadContext <--> ServerBootstrapAcceptor <--> TailContext
这里没有ChannelInitializer的原因是
ChannelInitializer只会调用一次,调用一次就会从pipeline中删除
3.channel.bind(localAddress,promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
注册任务代码:
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
这个task是ServerBoostrap在执行完initAndRegister()方法之后调用doBind0()方法,给nioEventLoop的taskQueue添加了这个task,这个task主要的逻辑有两个:
(1)从pipeline的tail向头调用bind()方法,最后在head去调用java nio的ServerSocketChannel去绑定端口号
channel.bind(localAddress, promise)
--> pipeline.bind(localAddress, promise)
----> tail.bind(localAddress, promise)
------> head.bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
------> unsafe.bind(localAddress, promise)
--------> AbstarctChannel.doBind(localAddress)
----------> javaChannel().bind(localAddress, config.getBacklog());
最后给eventLoop提交一个任务
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
(2)向eventLoop提交pipeline.fireChannelActive();任务
eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
注意:channel.bind(localAddress, promise)这个方法会调用pipeline的bind方法,它会从tail开始找有bind()方法的handler去执行,如果这时候你给pipeline添加了一个有bind()方法的handler,可能会导致执行不到javaChannel().bind(localAddress, config.getBacklog()); 最后不能绑定端口,client不能连接server
4. pipeline.fireChannelActive();
注册任务代码:
// ServerBoostrap 在bind时调用doBind0()方法会执行到下面这个方法
// io.netty.channel.AbstractChannel.AbstractUnsafe#bind
eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
这个pipeline.fireChannelActive()方法会从head到tail遍历所有pipeline中的handler去调用channelActive() 方法,此时pipeline中的handler有:
HeadContext <--> ServerBootstrapAcceptor <--> TailContext
分别看一下这个3个的channelActive方法
(1)HeadContext
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.fireChannelActive();
readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (channel.config().isAutoRead()) {
channel.read();
}
}
// io.netty.channel.AbstractChannel#read
// channel.read();
public Channel read() {
pipeline.read();
return this;
}
// io.netty.channel.DefaultChannelPipeline#read
// pipeline.read()
public final ChannelPipeline read() {
tail.read();
return this;
}
// io.netty.channel.AbstractChannelHandlerContext#read
// tail.read()
public ChannelHandlerContext read() {
// 找到有read()方法的AbstractChannelHandlerContext
final AbstractChannelHandlerContext next = findContextOutbound(MASK_READ);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
// 调用read()
next.invokeRead();
} else {
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(tasks.invokeReadTask);
}
return this;
}
private void invokeRead() {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).read(this);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
read();
}
}
// 最后会调用到HeadContext的read()方法
public void read(ChannelHandlerContext ctx) {
unsafe.beginRead();
}
// unsafe.beginRead() 会调用到这个方法
// io.netty.channel.AbstractChannel.AbstractUnsafe#beginRead
public final void beginRead() {
assertEventLoop();
// 如果还没有激活,即没有绑定端口,则返回
if (!isActive()) {
return;
}
try {
// 开始读,即真正意义上的开始接受事件
// 这里对于NioServerSocketChannel来说,会注册Accept事件到selector中
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
// 在这里,才是真正意义上的开启NioServerSocketChannel的accept事件
// 如果是NioSocketChannel,这里是开启read事件
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
selectionKey.interestOps(interestOps | readInterestOp);
}
}
这个fireChannelActive方法一定会调用HeadContext的channelActive方法,因为HeadContext就是第一个哈哈
public final ChannelPipeline fireChannelActive() {
AbstractChannelHandlerContext.invokeChannelActive(head);
return this;
}
注意:对于Netty Server端来说,必须走这段逻辑,否则Client完全会连不上Server,所以尽量不要给Server的bossEventLoop的pipeline中添加额外的handler,如果一定要加,那你必须继续触发bind()和read()操作
(2)ServerBootstrapAcceptor
这个handler没有channelActive方法跳过,不会调用到这个handler
(3)TailContext
// 调用到TailContext里的方法时,说明我们没有添加自己的channelActive方法
// 这是一个兜底操作
@Override
public void channelActive(ChannelHandlerContext ctx) {
onUnhandledInboundChannelActive();
}
protected void onUnhandledInboundChannelActive() {
}