无论是服务端还是客户端,都会有一步需要调用到父类AbstractBootStrap的initAndRegister()方法,将相应的channel注册到bossReactor线程池中去。
initAndRegister()方法主要分为3个部分:
-
创建Channel
-
绑定用户自定义的Handler()
-
将该Channel注册到Reactor中去
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = this.channelFactory.newChannel();//创建一个Channel
this.init(channel);//初始化Handler(),由子类bootstrap或者serverBootStrap进行实现,可视为一个模板方法
} catch (Throwable var3) {
if (channel != null) {
channel.unsafe().closeForcibly();
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
//还没有注册到线程池。使用默认线程GlobalEventExecutor
return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
// 将channel注册到Reactor线程池
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
三个步骤一个步骤一个步骤的解析:
1:通过工厂方法创建对应的channel。
channel = this.channelFactory.newChannel();
看名字就知道是工厂模式产生channel了。在serverBootStrap中是产生一个 NioServerSocketChannel,在bootStrap中是产生一个 NioSocketChannel。具体的生成实例是由channelFactory控制的,而serverBootStrap和bootStrap中的channelFactory类型是不一样的,是在bootstrap或者serverBootStrap的初始化时进行初始化的。如下图,在.channel中,通过传入的参数来控制ChannelFactory的类型。
=========================================this.channelFactory.newChannel() ===========================================================
channelFactory对象是我们的serverBootStrap.channel初始化的。
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
此工厂对象是ReflectiveChannelFactory实例
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
}
所以 channelFactory.newChannel() 实例化其实就是NioServerSocketChannel无参构造方法反射而成。
====================================this.channelFactory.newChannel() 结束,生成一个NioServerSocketChannel实例==============================================================
===================== NioServerSocketChannel实例化==================================================================
以NioServerSocketChannel为例:
-
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
-
//有两个步骤 1:打开一个默认的多路复用器辅助类 2:新建一个ServerSocketChannel实例
-
public NioServerSocketChannel() {
-
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
-
}
-
-
private static ServerSocketChannel newSocket(SelectorProvider provider) {
-
try {
-
// 打开一个ServerSocketChannel
-
return provider.openServerSocketChannel();
-
} catch (IOException e) {
-
throw new ChannelException(
-
"Failed to open a server socket.", e);
-
}
-
}
-
-
public NioServerSocketChannel(ServerSocketChannel channel) {
-
// 只对OP_ACCEPT事件感兴趣, 因为是服务端新生成的channel,第一个参数指定为null,表示没有父channel,第二个参数指定为ServerSocketChannel,第三个参数指定ServerSocketChannel关心的事件类型为
-
//SelectionKey.OP_ACCEPT。
-
super(null, channel, 16(SelectionKey.OP_ACCEPT)); //SelectionKey.OP_ACCEPT就是16,就是1往左移4位
-
// 初始化连接对应的配置
-
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
-
}
-
-
//上面super调用的父类方法
-
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
-
super(parent);
-
//将serverSocketChannel对象保存
-
this.ch = ch;
-
//设置关心的事件
-
this.readInterestOp = readInterestOp;
-
try {
-
// 将ServerSocketChannel设置为非阻塞模式
-
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);
-
}
-
}
在AbstractNioChannel中做了下面几件事:
1、继续调用父类AbstractChannel(Channel parent)构造方法;
2、通过this.ch = ch 保存ServerSocketChannel, 因为NioServerSocketChannel是Netty封装的对象,而ServerSocketChannel是有前面默认selector_provider生成的,是java nio的, 其实“this.ch = ch”可以被认为是绑定java nio服务端通道至netty对象中;
3、设置ServerSocketChannel关心的事件类型;
4、设置ServerSocketChannel为非阻塞的(熟悉Java NIO的都知道如果不设置为false,启动多路复用器会报异常)
-
-
//上面super调用的父类方法
-
protected AbstractChannel(Channel parent) {
-
this.parent = parent;
-
// 非配id,该id全局唯一
-
id = DefaultChannelId.newInstance();
-
// 初始化Unsafe, Server生成的Unsafe类为NioMessageUnsafe,Unsafe属于较底层的操作,不对应用开放
-
// 它处理的各种操作:register、bind、connect、disconnect、close、deregister,beginRead、write、flush
-
unsafe = newUnsafe();
-
// 创建pipeline
-
pipeline = new DefaultChannelPipeline(this);
-
}
newUnsafe()方法调用
在AbstractChannel类中,newUnsafe()是一个抽象方法
protected abstract AbstractUnsafe newUnsafe();
们找到AbstractNioMessageChannel类中有newUnsafe()的实现
@Overrideprotected AbstractNioUnsafe newUnsafe() {
return new NioMessageUnsafe();
}
此方法返回一个NioMessageUnsafe实例对象,而NioMessageUnsafe是AbstractNioMessageChannel的内部类:
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
====================unsafa初始化结束=================
====================pipeline初始化=======================
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
succeededFuture = new SucceededChannelFuture(channel, null);
voidPromise = new VoidChannelPromise(channel, true);
tail = new TailContext(this);
head = new HeadContext(this);
head.next = tail;
tail.prev = head;
}
=========================pipeline初始化结束====================
protected DefaultChannelPipeline(Channel channel) {
this.channel = (Channel)ObjectUtil.checkNotNull(channel, "channel");
this.succeededFuture = new SucceededChannelFuture(channel, (EventExecutor)null);
this.voidPromise = new VoidChannelPromise(channel, true);
this.tail = new DefaultChannelPipeline.TailContext(this);
this.head = new DefaultChannelPipeline.HeadContext(this);
this.head.next = this.tail;
this.tail.prev = this.head;
}
此DefaultChannelPipeline对象会绑定NioServerSocketChannel对象,并初始化了HeadContext及TailContext对象。
final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
TailContext(DefaultChannelPipeline pipeline) {
super(pipeline, (EventExecutor)null, DefaultChannelPipeline.TAIL_NAME, true, false);
this.setAddComplete();
}
final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler {
private final Unsafe unsafe;
HeadContext(DefaultChannelPipeline pipeline) {
super(pipeline, (EventExecutor)null, DefaultChannelPipeline.HEAD_NAME, false, true);
this.unsafe = pipeline.channel().unsafe();
this.setAddComplete();
}
由以上源码分析可知 channel = this.channelFactory.newChannel()方法会通过反射产生一个severSocketChannel实例,该实例在实例化过程中会默认监听accept事件(16),同时设置为非阻塞的,
(channel里就有unsafe以及pipeline(tail以及head都是在此时初始化的),这就会随着channel的初始化而初始化)
Socketchannel实例的产生和上面的一样,只不过注册是的事件是1也就是read事件,而不是accept事件
Channel初始化总结:
1、NioServerSocketChannel对象内部绑定了Java NIO创建的ServerSocketChannel对象;
2、Netty中,每个channel都有一个unsafe对象,此对象封装了Java NIO底层channel的操作细节;
3、Netty中,每个channel都有一个pipeline对象,此对象就是一个双向链表
=========================================Channel实例化产生结束=========================================================================
第二步 绑定用户的handler init()方法 具体可看前面的serverbootStrap的init方法或者bootstrap的init方法(包括handler的添加)。
主要作用就是:
-
设置serverChannel的选项参数
-
设置serverChannel的属性键值对
-
添加处理serverChannel的IO事件处理器
仅仅在serverBootStrap中有所不同,在第三步会在最后添加一个其内部类ServerBootstrapAcceptor的handler事件。这个handler是一个inbound事件,会在accept触发之后,由于管道的传递性,会将此channel注册到workerGroup中去。(获取连接后,进行初始化)
===============================================init()方法结束====================================================
第三步将channel(服务端则是serversocketchannel)绑定到boss线程:
ChannelFuture regFuture = this.config().group().register(channel);
this,config()是ServerBootStrap或者bootStrap实现的,返回其本身实例,主要是为了暴露出各种参数,配置
this,config().group可以看作为serverbootstrap/bootstrap.group()即eventloopGroup.register(channel),注意这个eventloopgroup是bossGroup,这是因为要将上面初始化好的channel注册到bossChannel上,而且整个initAndRegister方法是在AbstractBootStrap类里的,而在初始化时,此group是bossgroup,也就是NioEventLoopGroup
NioEventLoopGroup的register()方法是其父类MultithreadEventLoopGroup实现的。
public ChannelFuture register(Channel channel) {
return this.next().register(channel);
}
public EventExecutor next() {
return this.chooser.next();
}
chooser类是在一开始初始化NioEventLoopGroup时初始化的,是用来在EventLoopGroup选择一个eventLoop的策略,是2的幂次方就是&否则是%。(具体的EventLoop源码在后面有一个专门的章节进行分析)
1、children:EventExecutor数组,保存eventLoop。
2、chooser:从children中选取一个eventLoop的策略
最终调用了NioEventLoop的register()方法,最终调用的是:channel.unsafe().register(this, promise);(这个方法是在abstractChannel里执行的,在上面的channelfactory里已经初始化过了) 这行代码最终会调用AbstractChannel.AbstractUnsafe.register(EventLoop eventLoop, final ChannelPromise promise)方法:(具体的EventLoop源码在后面有一个专门的章节进行分析,在这里就是直接说出结果调用unsafe的register)。
其次中间应该会调用singleThreadEventLoop的register执行了eventloop的线程,进行轮询操作,反正是在注册之后就开始轮询了
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
} else if (AbstractChannel.this.isRegistered()) {//判断是否已经注册过了,即一个channel只能对用一个eventloop
promise.setFailure(new IllegalStateException("registered to an event loop already"));
} else if (!AbstractChannel.this.isCompatible(eventLoop)) {//判断是否兼容
promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
} else {
AbstractChannel.this.eventLoop = eventLoop;//该channel持有一个eventLoop线程,注册到channel
if (eventLoop.inEventLoop()) {//判断当前线程是否是EventloopGroup里的线程
this.register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
public void run() {
AbstractUnsafe.this.register0(promise);
}
});
} catch (Throwable var4) {
AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var4);
}
}
}
}
AbstractChannel.AbstractUnsafe.register0():
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
return;
}
boolean firstRegistration = this.neverRegistered;
//真正的注册方法
AbstractChannel.this.doRegister();
this.neverRegistered = false;//注册过了
AbstractChannel.this.registered = true;//表示已经注册到了eventloop中去了。
AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();//表示可以接收新的任务了?
this.safeSetSuccess(promise);
//触发channel的注册事件
//Netty的HeadHandler不需要处理ChannelRegistered事件,所以,直接调用下一个Handler,当ChannelRegistered事件传递到TailHandler后结束,TailHandler也不关心ChannelRegistered事件
AbstractChannel.this.pipeline.fireChannelRegistered();
// 只有从未注册的channel才会触发channelActive,避免连接注销并重新注册时多次触发channelActive。
// 注意后面还会出现fireChannelActive方法的调用,正常的第一次启动应该是触发后面那个fireChannelActive而不是这个
if (AbstractChannel.this.isActive()) {//判断是否已经绑定了 其中isActive()是一个多态方法,如果是服务端则,判断监听是否启动;如果是客户端,判断TCP连接是否完成。ChannelActive事件在ChannelPipeline()传递,完成之后根据配置决定是否自动触发Channel的读操作。
if (firstRegistration) {//是否是第一次绑定
// 这里和前面的ServerSocketChannel分析一样,最终会触发unsafe.beginRead()
AbstractChannel.this.pipeline.fireChannelActive();
} else if (AbstractChannel.this.config().isAutoRead()) {
this.beginRead();
}
}
} catch (Throwable var3) {
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var3);
}
}
=================================注册方法分析===================================================
最终最终的注册方法:AbstractNioChannel.doRegister():
protected void doRegister() throws Exception {
boolean selected = false;
while(true) {
try {
this.selectionKey = this.javaChannel().register(this.eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException var3) {
// 如果发生异常则强制执行Selector.selectNow()方法使 "canceled"的SelectionKey从Selector中移除
if (selected) {
throw var3;
}
// 强制Selector调用select now,防止取消的SelectionKey未真正取消(因为还没有调用到Select.select(..))
this.eventLoop().selectNow();
selected = true;
}
}
}
循环执行selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
一直到成功为止。
在abstractNiochannel类中实现:
protected SelectableChannel javaChannel() {
return this.ch;
}
private final SelectableChannel ch;
在abstractNiochannel类中实现:
register(eventLoop().unwrappedSelector(), 0, this);是在 AbstractSelectableChannel实现的
public final SelectionKey register(Selector var1, int var2, Object var3) throws ClosedChannelException {
Object var4 = this.regLock;
synchronized(this.regLock) {
if (!this.isOpen()) {
throw new ClosedChannelException();
} else if ((var2 & ~this.validOps()) != 0) {
throw new IllegalArgumentException();
} else if (this.blocking) {
throw new IllegalBlockingModeException();
} else {
SelectionKey var5 = this.findKey(var1);
if (var5 != null) {
var5.interestOps(var2);
var5.attach(var3);
}
if (var5 == null) {
Object var6 = this.keyLock;
synchronized(this.keyLock) {
if (!this.isOpen()) {
throw new ClosedChannelException();
}
var5 = ((AbstractSelector)var1).register(this, var2, var3); //注册的参数分别是channel,事件var2,event
this.addKey(var5);
}
}
return var5;
}
}
}
大伙儿可能会很诧异,应该注册OP_ACCEPT(16)到多路复用器上,怎么注册0呢?0表示只注册,不监听任何网络操作。这样做的原因如下:
-
注册方法是多态的,它既可以被NioServerSocketChannel用来监听客户端的连接接入,也可以用来注册SocketChannel,用来监听网络读或者写操作;
-
通过SelectionKey的interestOps(int ops)方法可以方便的修改监听操作位。所以,此处注册需要获取SelectionKey并给AbstractNioChannel的成员变量selectionKey赋值
======================注册方法分析结束=================================================
不管是注册到boss线程还是work线程,都会在注册成功之后调用AbstractChannel.this.pipeline.fireChannelRegistered();方法
以HttpSnoopServer为例,childHandler为HttpSnoopServerInitializer,初始化是NioSocketChannel的pipeline中只有tail,head及HttpSnoopServerInitializer的实例,
tail和head中的channelRegisterd除了传递或者停止外没有其他用处,因此此处的channelRegisterd主要逻辑在HttpSnoopServerInitializer的channelRegistered方法:
* public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
* ChannelPipeline pipeline = ctx.pipeline();
* boolean success = false;
* try {
* // initChannel主要是初始化channel对应的pipeline
* initChannel((C) ctx.channel());
* // 初始化完成后将自身移除
* pipeline.remove(this);
* // channelRegistered属于inbound事件,注册后调用一次该方法,这样所有用户添加的handler可以感知到此事件
* ctx.fireChannelRegistered();
* success = true;
* } catch (Throwable t) {
* logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
* } finally {
* if (pipeline.context(this) != null) {
* pipeline.remove(this);
* }
* if (!success) {
* ctx.close();
* }
* }
* }
* // 此方法在HttpSnoopServerInitializer中
* public void initChannel(SocketChannel ch) {
* ChannelPipeline p = ch.pipeline();
* if (sslCtx != null) {
* p.addLast(sslCtx.newHandler(ch.alloc()));
* }
* p.addLast(new HttpRequestDecoder());
* // Uncomment the following line if you don't want to handle HttpChunks.
* //p.addLast(new HttpObjectAggregator(1048576));
* p.addLast(new HttpResponseEncoder());
* // Remove the following line if you don't want automatic content compression.
* //p.addLast(new HttpContentCompressor());
* p.addLast(new HttpSnoopServerHandler());
* }
fireChannelRegistered()调用的仍然是beginRead()方法
================前面的注册之后就开始读this.beginRead();===================================
public final void beginRead() {
assertEventLoop();
if (!isActive()) {
return;
}
try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
}
由于不同类型的Channel对读的准备工作不同,因此doBeginRead也是个多态方法。
对于NIO通信,无论是客户端,还是服务端,都要设置网络监听操作位为自己感兴趣的,对于NioServerSocketChannel感兴趣的是 OP_ACCEPT(16),于是修改操作位
刚开始是注册了accept(16),后来在accept之后,就又会进行一次管道的传递,调用这个方法,进行设置为reead事件了
@Override
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);//一个是0一个是前面的16,最终仍然是16
}
}
============================this.beginRead()结束==================================================
该实现是通过spi的机制实现的,已经到了最底层了
==================================到这儿就是注册完成了====================================