文章目录
探究源码
启动流程
nio启动流程
因为netty的底层使用的是nio,所以先回忆一下nio的启动流程对于接下来要探究的netty启动流程也是有好处的。
-
创建一个选择器,监听多个channel发生的各类事件
Selector selector = Selector.open();
-
创建一个ServerSocketChannel,并且设置非阻塞
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false);
-
将serverSocketChannel注册进选择器中
SelectionKey selectionKey = serverSocketChannel.register(selector, 0, null);
这是jdk原生的ServerSocketChannel,将来如果selector发生了事件,会将这个事件交给Nio相应的类去处理,这里就使用到了attachment附件,通过附件将serverSocketChannel与NioServerSocketChannel进行绑定。
NioServerSocketChannel nioServerSocketChannel = new NioServerSocketChannel(); SelectionKey selectionKey = serverSocketChannel.register(selector, 0, attachment);
-
绑定端口
serverSocketChannel.bid(new InetSocketAddress(8080));
-
在selectionKey上注册一个它关心的事件类型
selectionKey.interestOps(SelectionKey.OP_ACCEPT);
概述
上面nio的五个步骤是如何在netty中实现的?
下方代码是使用netty创建一个服务器的基本步骤
new ServerBootstrap()
.group(new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new StringDecoder());
}
})
.bind(8080);
我们知道EventLoop包含了一个Selector和一个单线程执行器 ,也就是说.group(new NioEventLoopGroup())
这行语句可以看为是完成Nio的第一步创建一个选择器的。
Nio剩下的四个步骤其实都是在.bind(8080);
这行语句完成的,然后我们点进bind()方法,接着会进入到第一个比较重要的方法doBind
private ChannelFuture doBind(final SocketAddress localAddress) {
// initAndRegister()方法 所做的事情就是初始化和注册,相当于上面Nio的第二步和第三步
// 它会将ServerSocketChannel创建好后注册进Selector中。该方法返回一个Future对象,就说明该方法是异步的,
final ChannelFuture regFuture = this.initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
// 这里就会利用future对象调用isDone()进行判断,如果上面initAndRegister()方法干的活比较快,就会执行if语句,
// 但是一般情况下initAndRegister()方法中的nio线程将ServerSocketChannel和Selector进行绑定会比较慢 会执行else语句
} else if (regFuture.isDone()) {
ChannelPromise promise = channel.newPromise();
// doBind0()方法是相当于Nio的第四步 绑定端口,监听事件
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
final AbstractBootstrap.PendingRegistrationPromise promise = new AbstractBootstrap.PendingRegistrationPromise(channel);
// 从这里可以看出future对象采用了异步的方式执行下面的语句,下方的doBind0()方法也就不是主线程调用了
regFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
// 进入到else语句后会在这里执行doBind0()方法
AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
在正式开始之前需要了解ServerBootstrap.bind(8080);
是主线程调用的,然后进入到doBind()
方法 在进入到initAndRegister()
方法中,直到创建ServerSocketChannel都是主线程做的事,包括register的前一部分都是主线程,但是在Register中会启动Nio线程,后续的操作就不是在主线程中执行了,ServerSocketChannel注册进Selector中都是Nio线程做的事,如下图所示:
概述需要了解的就几件事
- init是创建ServerSocketChannel
- Register是将ServerSocketChannel注册进Selector中的,是nio线程执行的
- initAndRegister()会返回一个future对象,然后使用该对象进行if判断,一般情况下都会进入到else语句
- else语句中会利用future的异步方式,通过nio线程来执行doBind0()方法
init
接下来详细了解initAndRegister()
方法中的init部分。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
// 这行就是创建一个channel,创建的就是NioServerSocketChannel。
// 再点进newChannel()方法就会发现里面是利用了反射调用无参构造方法获取的对象 constructor.newInstance()
// 这里不仅仅会创建NioServerSocketChannel。还会创建jdk的ServerSocketChannel
channel = this.channelFactory.newChannel();
// 创建NioServerSocketChannel对象后就调用了init()方法,具体方法如下方代码所示
this.init(channel);
} catch (Throwable var3) {
if (channel != null) {
channel.unsafe().closeForcibly();
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
// 上面的init部分 从这里开始就是register部分了
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
创建NioServerSocketChannel对象后就调用了init()方法
void init(Channel channel) throws Exception {
。。。
ChannelPipeline p = channel.pipeline();
。。。
// 这里就会发现创建NioServerSocketChannel后会往该channel的pipeline中添加一个Handler
// 这个ChannelHandler和其他hander不同的地方在于该handler的initChannel()方法只会执行一次
// 这里执行往pipeline中添加handler哦 还没有到执行的地步哦
p.addLast(new ChannelHandler[]{
new ChannelInitializer<Channel>() {
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = ServerBootstrap.this.config.handler();
if (handler != null) {
pipeline.addLast(new ChannelHandler[]{
handler});
}
ch.eventLoop().execute(new Runnable() {
public void run() {
pipeline.addLast(new ChannelHandler[]{
new ServerBootstrap.ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
}
});
}
}});
}
所以initAndRegister()
方法中的init部分的作用就是
- 创建了一个NioServerSocketChannel,
- 并往该channel的pipeline中添加了一个Handler。
Register
接下来就轮到了Register部分
final ChannelFuture initAndRegister() {
Channel channel = null;
// init
try {
channel = this.channelFactory.newChannel();
this.init(channel);
} catch (Throwable var3) {
if (channel != null) {
channel.unsafe().closeForcibly();
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
return (new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
// 上面的init部分 从这里开始就是register部分了
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
首先是register部分的第一行代码ChannelFuture regFuture = this.config().group().register(channel);
该方法返回的是有个ChannelFuture对象,那么我们笃定该方法是异步的。然后我们点进该方法,多点几次就会进入到核心
经过的类如下图
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
} else if (AbstractChannel.this.isRegistered()) {
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;
// 到目前为止都是主线程在执行,下面这个if就是判断当前线程是否是nio线程,所以自然而然就进入到else语句中
if (eventLoop.inEventLoop()) {
this.register0(promise);
} else {
// 这里做的事情就是将正在做事的register0(promise)方法封装到了任务对象中,然后让eventLoop线程去执行
try {
// 这里第一次调用execute()方法会创建EventLoop线程,然后取执行run()方法,而不是早就创建好线程直接用。
eventLoop.execute(new Runnable() {
public void run() {
AbstractUnsafe.this.