ChannelFactory
上文说到,channel
方法创建了ReflectiveChannelFactory负责创建NioSocketChannel或者NioServerSocketChannel实例。创建实例的时候就是通过BootstrapChannelFactory的newChannel来创建。
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
NioSocketChannel的基类
我们首先看看它们NioSocketChannel的基类(同时也是NioServerSocketChannel的基类)AbstractNioChannel
和AbstractChannel
(后者又是前者的基类)中都有哪些东西,可以帮助我们理解netty的channel中有哪些基本元素。
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
// SelectableChannel 应该就是对应着NIO中的ServerSocketChannel或者SocketChannel实例
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
protected AbstractChannel(Channel parent) {
this.parent = parent;
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
我们可以看出,不论是NioServerSocketChannel还是NioSocketChannel,每一个channel都有父channel(这个后边会提到),一个unsafe实例、一个pipeline实例(这个很重要),另外还有它所对应的NIO中的SelectableChannel,猜测一下大概也就是NioServerSocketChannel对应着ServerSocketChannel,NioSocketChannel对应着SocketChannel,并且还配置了读事件,并设置NIO的channel为非阻塞形式,毕竟这里使用的都是Nio开头的channel嘛。
NioSocketChannel及创建
NioSocketChannel
的创建源自于client端的connect
操作。关于connect
操作的具体过程,我们先不展开,只是介绍一下NioSocketChannel是如何创建的。connect
方法会调用到doResolveAndConnect
方法,其中的initAndRegister()
(位于AbstractBootstrap
)就完成了NioSocketChannel的创建。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory().newChannel();
init(channel);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
ChannelFuture regFuture = group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
从整体上来看,这个方法完成了通过ChannelFactory创建NioSocketChannel实例,完成初始化并且注册到事件循环组中。
ChannelFactory(实际就是之前提到的ReflectiveChannelFactory)会通过反射方式利用无参构造器创建NioSocketChannel,我们跟踪它的无参构造器,会发现最终会调用下面的构造方法,传入的parent是null(顺便提前提一下,在client端,每个NioSocketChannel是没有父channel的,但是在Server端就不一样了,我会在server端的时候再讲),然后传入了一个Java NIO中的SocketChannel实例,这样就已经建立起了netty中的NioSocketChannel和NIO中SocketChannel的联系,本质上来说netty中的Channel不等于NIO中的Channel,而他们之间的联系和区别大概就如同这里所说的这样,NioSocketChannel(准确说是它的基类AbstractNioChannel
)中封装了一个SocketChannel。
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
同时还要注意的一点,也在上文提到过,每个NioSocketChannel实例都会封装一个pipeline实例
(在基类AbstractChannel
中),看源码可以知道,这个实例就是DefaultChannelPipeline
。
NioSocketChannel的初始化及注册
上文所讲为initAndRegister()
方法先通过ChannelFactory创建了NioSocketChannel实例,紧接着会调用init(channel)
方法对创建的channel进行初始化。这里初始化的方法被子类BootStrap具体实现。
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline();
p.addLast(handler());
final Map, Object> options = options();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map, Object> attrs = attrs();
synchronized (attrs) {
for (Entry, Object> e: attrs.entrySet()) {
channel.attr((AttributeKey
在init
方法中,向NioSocketChannel中封装的pipeline中添加(addLast)了一个Handler,而这个handler就是在执行BootStrap.handler(xxx)时传入的Handler,具体来说就是ChannelInitializer
,随后又配置了一些options和attributes。
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
// 配置需要的处理器,也是交给了Bootstrap管理
.handler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
xxxxxxxxxxxxxxxxxxx
}
});
initAndRegister
方法执行init(channel)
方法之后,开始完成NioSocketChannel向事件循环组的注册。
ChannelFuture regFuture = group().register(channel);
这里的注册是通过调用事件循环组的register方法来完成,回想一下,我们在client端的启动代码中写到,我们用的事件循环组是NioEventLoopGroup,我们找到它的register(channel)方法(实际位于MultithreadEventLoopGroup
),最终他调到的是
next().register(channel);
而next方法,最终实际调用的就是chooser(GenericEventExecutorChooser
或PowerOfTwoEventExecutorChooser
)的next方法,从NioEventLoopGroup的children数组字段中选择出一个NioEventLoop。而register就是调用的选择出的NioEventLoop的register方法(这个方法实际位于它的基类SingleThreadEventLoop
)。
@Override
public ChannelFuture register(Channel channel) {
return register(channel, new DefaultChannelPromise(channel, this));
}
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
/*省略部分代码*/
channel.unsafe().register(this, promise);
return promise;
}
注册的时候先将当前NioSocketChannel和当前EventLoop封装在一个DefaultChannelPromise中,然后调用通过NioSocketChannel中的unsafe实例完成注册(实现在AbstractUnsafe
中)。
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
/*省略一些代码*/
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
private void register0(ChannelPromise promise) {
try {
boolean firstRegistration = neverRegistered;
// 重点方法
doRegister();
neverRegistered = false;
registered = true;
/*后边的代码我们暂时先不看*/
}
} catch (Throwable t) {
/*省略的代码*/
}
}
// 方法位于AbstractNioChannel
@Override
protected void doRegister() throws Exception {
boolean selected = false;
for (;;) {
try {
// javaChannel 返回的就是 当前netty中的channel所关联的java NIO中的channel
// 具体来说就是 netty的NioSocketChannel中关联的NIO中的SocketChannel
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
if (!selected) {
// Force the Selector to select now as the "canceled" SelectionKey may still be
// cached and not removed because no Select.select(..) operation was called yet.
eventLoop().selectNow();
selected = true;
} else {
// We forced a select operation on the selector before but the SelectionKey is still cached
// for whatever reason. JDK bug ?
throw e;
}
}
}
}
我们分析一下上面的代码:在执行注册时,实际就是完成了NioSocketChannel关联的NIO中的SocketChannel向NioEventLoop关联的NIO中的Selector的注册(但是这个时候并没有注册有效的事件,因为传入的参数为0)。那么注册工作是交由对应的NioEventLoop中的线程来完成,如果不是的话(因为此时线程有可能就是从启动代码一路直行过来的主线程),就封装成一个Runnable任务塞入NioEventLoop的execute方法,通过前面的文章知道,这个任务会被添加到任务队列中,由NioEventLoop中的线程来完成,而这个过程是个异步过程。
NioSocketChannel创建及注册过程总结
总结一下NioSocketChannel的创建初始化及注册的过程:创建的NioSocketChannel,都会包含一个pipeline实例,一个unsafe实例,一个NIO中的SocketChannel实例,以及这个SocketChannel所对应的SelectionKey(在channel注册阶段赋值)。创建好NioSocketChannel后,进行初始化(init方法),将在启动代码中handler(xxxx)方法中的ChannelInitializer这个Handler添加进入关联的pipeline中(addLast方法添加)。随后再开始NioSocketChannel的注册,注册的流程简图如下。
NioServerSocketChannel
上文理解了之后,在看NioServerSocketChannel,就比较容易了,与NioSocketChannel不同的是,NioServerSocketChannel中传入的是Accept事件,而NioSocketChannel中为读事件。
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
this.readInterestOp = readInterestOp;
作为类比,NioServerSocketChannel的注册源自于bind方法的执行,与NioSocketChannel不同点在于initAndRegister()
中的init(channel)
方法,服务端的NioServerSocketChannel的init方法被ServerBootStrap重写。
@Override
void init(Channel channel) throws Exception {
final Map, Object> options = options();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map, Object> attrs = attrs();
synchronized (attrs) {
for (Entry, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey
server端的init方法相比于client端稍微复杂一些,它向NioServerSocketChannel的pipeline中添加了一个ChannelInitializer处理器,负责后续添加父Handler(在启动代码中对应的就是LoggingHandler)。并且向通道所在的循环组添加任务,用于将关联了子处理器的ServerBootstrapAcceptor添加到pipeline中。
b.handler(new LoggingHandler(LogLevel.INFO))
其余的部分与NioSocketChannel相似,就不在多说了,不过稍微提一下的一个点是,NioServerSocketChannel的注册既然跟NioSocketChannel相似,那么在进行注册的时候都是向AbstractBootStrap的group所代表的事件循环组注册的,也就是在server端,NioServerSocketChannel需要向父循环组中某个NioEventLoop关联的Selector进行注册。
*链接
1. Netty解析:第一个demo——Echo Server
2. Netty解析:NioEventLoopGroup事件循环组
3. Netty解析:NioSocketChannel、NioServerSocketChannel的创建及注册
4. Netty解析:Handler、Pipeline大动脉及其在注册过程中体现
5. Netty解析:connect/bind方法背后
6. Netty解析:服务端如何接受连接并后续处理读写事件