目录
ChannelFuture regFuture = this.config().group().register(channel); --- 重要
传统NIO初始化步骤
1. ServerSocketChannel server = ServerSocketChannel.open();
// 通知服务地址IP/Port
2. server.bind(new InetSocketAddress(this.port));
// NIO兼容BIO 默认是阻塞的IO模型 所以要通知server开启非阻塞IO模型
3. server.configureBlocking(false); // 是否阻塞设为false
// 大堂经理准备就绪 接客
4. selector = Selector.open();
// 注册 等待
5. server.register(selector, SelectionKey.OP_ACCEPT);
以上是传统NIO初始化的常规步骤,而Netty实质是对传统NIO模型的高度封装,所以Netty的底层调用的其实还是NIO的相关API。
Netty初始化时序图
Netty初始化入口
// Netty入口 port 绑定端口,当不设置IP时,默认使用 localHost 作为IP地址
// 服务绑定端口
ChannelFuture future = server.bind(port).sync();
AbstractBootstrap
1.
public ChannelFuture bind(int inetPort) {
return this.bind(new InetSocketAddress(inetPort));
}
2.
public ChannelFuture bind(SocketAddress localAddress) {
this.validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
} else {
return this.doBind(localAddress);
}
}
3.
// 真正调用的方法 doBind() 方法
private ChannelFuture doBind(final SocketAddress localAddress) {
// 初始和注册 (核心)
final ChannelFuture regFuture = this.initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
} else if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
final AbstractBootstrap.PendingRegistrationPromise promise = new AbstractBootstrap.PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
4.
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 创建Channel
channel = this.channelFactory.newChannel();
// 初始化
this.init(channel);
} catch (Throwable var3) {
if (channel != null) {
channel.unsafe().closeForcibly();
}
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
// 注册
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
ReflectiveChannelFactory
public T newChannel() {
try {
// 实质就是利用反射创建出 chaneel
return (Channel)this.clazz.newInstance();
} catch (Throwable var2) {
throw new ChannelException("Unable to create Channel from class " + this.clazz, var2);
}
}
那么这个反射出来的Channel到底是个什么呢?ServerBootStrap在初始化的时候我们设置过一个Channel信息,我们大胆擦测一下,这个是不是就是这里反射出来的呢?
我们可以发现,这个实质调用的是AbstractBootstrap的方法,
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
} else {
// 这里看着有设置参数的意思
// new ReflectiveChannelFactory(channelClass)) 这个不就是上文的类
return this.channelFactory((io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
}
}
new ReflectiveChannelFactory(channelClass)) 这个方法很眼熟,这不就是我们上面的工厂类么?点进去发现,原来在这里类信息已经加载掉了
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
} else {
// 在这里类信息已经加载了
this.clazz = clazz;
}
}
紧跟着,反射其实实质调用的是该类的无参构造,我们点入这个类的无参构造
NioServerSocketChannel
这个无参构造,其实里面有两个核心的方法,
newSocket() --- 重要
// 创建出JAVA的核心Channel 对应文章NIO原生API中的 步骤1
private static java.nio.channels.ServerSocketChannel newSocket(SelectorProvider provider) {
try {
//这里创建JDK的核心Channel
return provider.openServerSocketChannel();
} catch (IOException var2) {
throw new ChannelException("Failed to open a server socket.", var2);
}
}
this
public NioServerSocketChannel(java.nio.channels.ServerSocketChannel channel) {
//这里有两个重要的步骤 1
super((Channel)null, channel, 16);
// 设置 congfig 之后会用到
this.config = new NioServerSocketChannel.NioServerSocketChannelConfig(this, this.javaChannel().socket());
}
AbstractNioChannel -- 重要
这里对应的文章开头的3,开启NIO,设置BIO为false
ServerBootstrap
这里是Channel初始化完成,并开启了NIO模式,接下来就是config的设置
这里的config就对应上文中的 config,而options就是我们在server创建中所设置的一些参数,会在这里循环放入到config中,channel中的 attr参数是一个拓展参数,可以将我们需要用的属性,赋值在这里
以上部分就是初始化完成,接下来进入注册的环节
ChannelFuture regFuture = this.config().group().register(channel); --- 重要
这个方法在跟踪之后,实质调用的是 AbstractNioChannel 中的 doRegister方法
protected void doRegister() throws Exception {
boolean selected = false;
while(true) {
try {
// 这里完成了真正的注册
this.selectionKey = this.javaChannel().register(this.eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException var3) {
if (selected) {
throw var3;
}
this.eventLoop().selectNow();
selected = true;
}
}
}
this.javaChannel().register(this.eventLoop().selector, 0, this); 这个方法实质就是文章开头的 5 ,注册
注册完成之后,就剩最后一步,开启IP和端口, 这个比较深,我也是找了很久才找到这个方法 ,基本是找的凌乱了
文章开头中,我们找到AbstractBootstrap中的doBind()方法,这个方法中调用了一个doBind0()方法,这个方法,也是一个重要的方法
doBind0() -- 重要
private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {
channel.eventLoop().execute(new Runnable() {
public void run() {
if (regFuture.isSuccess()) {
// 这里进行IP和端口的绑定
channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
这里的bind的调用实质调用的是 NioServerSocketChannel 中的 doBind()方法
protected void doBind(SocketAddress localAddress) throws Exception {
// 这里对应的是文章开头的 2
// 判断当前JDK版本 决定用哪个IO模型
if (PlatformDependent.javaVersion() >= 7) {
this.javaChannel().bind(localAddress, this.config.getBacklog());
} else {
this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
}
}
总结
至此,Netty完成NIO的初始化已经全部完成,Netty封装的层级确实很深,找这些步骤真是很不容易,不画出这个时序图和与原生API对比,基本都是云里雾里没有任何头绪
之后的博文还会继续更新Netty源码的进一步分析。