Netty源码分析-启动流程
Netty源码分析-启动流程
原生NIO启动流程
// netty 中使用 NioEventLoopGroup (简称 nio boss 线程)来封装线程和 selector
Selector selector = Selector.open();
// 创建 NioServerSocketChannel,同时会初始化它关联的 handler,以及为原生 ssc 存储 config
NioServerSocketChannel attachment = new NioServerSocketChannel();
// 创建 NioServerSocketChannel 时,创建了 java 原生的 ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
// 启动 nio boss 线程执行接下来的操作
//注册(仅关联 selector 和 NioServerSocketChannel),未关注事件
SelectionKey selectionKey = serverSocketChannel.register(selector, 0, attachment);
// head -> 初始化器 -> ServerBootstrapAcceptor -> tail,初始化器是一次性的,只为添加 acceptor
// 绑定端口
serverSocketChannel.bind(new InetSocketAddress(8080));
// 触发 channel active 事件,在 head 中关注 op_accept 事件
selectionKey.interestOps(SelectionKey.OP_ACCEPT);
- 获得选择器Selector,Netty中使用NioEventloopGroup中的NioEventloop封装了线程和选择器
- 创建NioServerSocketChannel,该Channel作为附件添加到ServerSocketChannel中
- 创建ServerSocketChannel,将其设置为非阻塞模式,并注册到Selector中,此时未关注事件,但是添加了附件NioServerSocketChannel
- 绑定端口
- 通过interestOps设置感兴趣的事件
Netty启动流程
给出简易的Netty服务端的启动代码:
new ServerBootstrap()
.group(new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
nioSocketChannel.pipeline().addLast(new LoggingHandler());
}
}).bind(8080);
bind
选择器Selector的创建是在NioEventloopGroup
中完成的。NioServerSocketChannel
与ServerSocketChannel
的创建,ServerSocketChannel
注册到Selector中以及绑定操作都是由bind方法完成的。所以服务器启动的入口便是io.netty.bootstrap.ServerBootstrap.bind
。
public ChannelFuture bind(SocketAddress localAddress) {
validate();
return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}
真正完成初始化、注册以及绑定的方法是
重要源码:
private ChannelFuture doBind(final SocketAddress localAddress) {
// 负责NioServerSocketChannel和ServerSocketChannel的创建
// ServerSocketChannel的注册工作
// init由main线程完成,regisetr由NIO线程完成
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
// 因为register操作是异步的
// 所以要判断主线程执行到这里时,register操作是否已经执行完毕
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
// 执行doBind0绑定操作
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
// 如果register操作还没执行完,就会到这个分支中来
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
// 添加监听器,NIO线程异步进行doBind0操作
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
其中有两个重要的方法:initAndRegister()
和doBind0(regFuture,channel,localAddress,promise)
-
initAndRegister主要负责NioServerSocketChannel和ServerSocketChannel的创建(主线程中完成)与ServerSocketChannel注册(NIO线程中完成)工作
init
就对应原生NIO中的 :ServerSocketChannel ssc = ServerSocketChannel.open();
register
就对应原生NIO中的:SelectionKey selectionKey = ssc.register(selector,0,nettySsc);
-
doBind0则负责连接的创建工作,对应原生NIO中的 :
ssc.bind(new InetSocketAddress(8080,backlog));
线程执行:
init 中 创建NioServerSocketChannel --- main 线程
--->添加NioServerSocketChannel 初始化 handler --- main 线程
register 中 启动 nio boss 线程 --- main 线程
--->原生ssc注册至selector未关注事件 --- nio 线程
--->执行NioServerSocketChannel 初始化 handler --- nio 线程
regFuture 等待回调doBind0 --- nio 线程
--->原生 ServerSocketChannel 绑定 --- nio 线程
--->触发NioServerSocketChannel active 事件 --- nio 线程
initAndRegister
final ChannelFuture initAndRegister() {
Channel channel = null;
// 此处执行init操作
try {
// 标识1: 通过工厂获取到创建的NioServerScoketChannel
channel = this.channelFactory.newChannel();
// 标识2: 添加NioServerSocketChannel 初始化 handler
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);
}
// 标识3:
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
标识1:
通过new Channel();
创建channel,源码如下:
public T newChannel() {
try {
// 不难看出是通过反射进行channel的创建,在此处是获取到NioServerSocketChannel的构造
return (Channel)this.constructor.newInstance();
} catch (Throwable var2) {
throw new ChannelException("Unable to create Channel from class " + this.constructor.getDeclaringClass(), var2);
}
}
NioServerSocketChannel的构造:
public NioServerSocketChannel() {
// 创建了ServerSocketChannel实例
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
newSocket() :
private static ServerSocketChannel newSocket(SelectorProvider provider) {
try {
// 此处相当于ServerSocketChannel.open()
// 创建了ServerSocketChannel实例
return provider.openServerSocketChannel();
} catch (IOException e) {
throw new ChannelException("Failed to open a server socket.", e);
}
}
原生的ServerSocketChannel
中的open方法:
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
可以看到,Netty中的NioServerSocketChannel
与原生的ServerSocketChannel
都调用了provider.openServerSocketChannel()
来创建ServerSocketChannel实例。
至此,相当于完成了ServerSocketChannel ssc = ServerSocketChannel.open();
标识2:
init中重要源码部分:
@Override
void init(Channel channel) {
...
// NioSocketChannl的Pipeline
ChannelPipeline p = channel.pipeline();
...
// 向Pipeline中添加了一个handler,该handler等待被调用,ChannelInitializer 只会执行一次
p.addLast(new ChannelInitializer<Channel>() {
@Override
// register之后才调用该方法,可以在此添加断点通过debug的方式查看何时被调用
public void initChannel(final Channel ch) {
final ChannelPipeline pipeline = ch.pipeline();
// 创建handler并加入到pipeline中
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 添加新的handler,在发生Accept事件后建立连接
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
标识3:
该方法最终调用的是promise.channel().unsafe().register(this, promise)
方法:
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
...
// 获取EventLoop
AbstractChannel.this.eventLoop = eventLoop;
// 此处完成了由 主线程 到 NIO线程 的切换
// eventLoop.inEventLoop()用于判断当前线程是不是eventLoop线程中的线程,也就是判断当前线程是否为NIO线程
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
// 因为当前启动流程运行到这里是main线程,所以一定会进入到else中
try {
// 向eventLoop中的NIO线程添加任务,注意eventLoop为了节省资源,所以使用的是懒加载方式
// 只有第一次执行execute方法时,才会去真正创建eventLoop关联的NIO线程
eventLoop.execute(new Runnable() {
@Override
public void run() {
// 该方法中会执行doRegister
// 执行注册操作
register0(promise);
}
});
} catch (Throwable t) {
...
}
}
}
register0( ):
private void register0(ChannelPromise promise) {
try {
...
// 执行真正的注册操作
doRegister();
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
// 调用init中的initChannel方法
pipeline.invokeHandlerAddedIfNeeded();
// 向Promise中设置执行成功的结果,后续会通过promise的是否完成的值去选择执行doBind0()方法
safeSetSuccess(promise);
...
} catch (Throwable t) {
...
}
}
后续ChannelFuture regFuture = initAndRegister();
中的regFuture
就是safeSetSuccess
设置的promise
,可以通过debug进行标识以及查看对象值来确定。
一般来说,真正执行操作的方法名前都会去加do
,spring和Alibaba的源码中都是如此。
doRegister( ):
@Override
protected void doRegister() throws Exception {
boolean selected = false;
// 循环 CAS
for (;;) {
try {
// javaChannel()即为Java中原生的ServerSocketChannel
// eventLoop().unwrappedSelector()获取eventLoop中的Selector
// this为NIOServerSocketChannel,作为附件
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
return;
} catch (CancelledKeyException e) {
...
}
}
}
原生ssc注册至selector,此时并没用关注任何事件。此方法为NIO线程执行。
相当于执行了:SeletionKey selectionKey = ssc.register(selector,0,nettySsc);
返回至register0( )执行pipeline.invokeHandlerAddedIfNeeded();
回调之前的init
中的initChannel
方法。
bind
执行绑定操作,重要源码部分:
public final void bind(SocketAddress localAddress, ChannelPromise promise) {
this.assertEventLoop();
if (promise.setUncancellable() && this.ensureOpen(promise)) {
if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
AbstractChannel.logger.warn("A non-root user can't receive a broadcast packet if the socket is not bound to a wildcard address; binding to a non-wildcard address (" + localAddress + ") anyway as requested.");
}
boolean wasActive = AbstractChannel.this.isActive();
try {
// 真正执行 bind 绑定操作
AbstractChannel.this.doBind(localAddress);
} catch (Throwable var5) {
this.safeSetFailure(promise, var5);
this.closeIfClosed();
return;
}
// 判断之前的ServerSocketChannel是否已经可用为active状态
if (!wasActive && AbstractChannel.this.isActive()) {
this.invokeLater(new Runnable() {
public void run() {
// 触发这个ServerSocketChannel上的pipeline上的所有handler的active事件
AbstractChannel.this.pipeline.fireChannelActive();
}
});
}
this.safeSetSuccess(promise);
}
}
**doBind: **
@SuppressJava6Requirement(reason = "Usage guarded by java version check")
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
// 判断java版本是不是大于7
if (PlatformDependent.javaVersion() >= 7) {
// 调用ServerSocketChannel的bind方法,绑定端口
javaChannel().bind(localAddress, config.getBacklog());
} else {
javaChannel().socket().bind(localAddress, config.getBacklog());
}
}
javaChannel( )获取到的是ServerSocketChannel对象,使用原生的ServerSocketChannel进行绑定。
相当于执行了:ssc.bind(new InetSocketAddress(8080,backlog));
此时ServerSocketChannel 的 pipeline上拥有三个处理器:head->acceptor->tail
。会先调用到headContext中的channelActive方法:
在一系列的调用过后,最终会完成相当于:selectionKey.interestOps(SelectionKey,OP_ACCEPT);
的操作:
在AbstractNioChannel.doBeginRead()
方法中,会添加ServerSocketChannel添加Accept事件:
@Override
protected void doBeginRead() throws Exception {
// Channel.read() or ChannelHandlerContext.read() was called
final SelectionKey selectionKey = this.selectionKey;
if (!selectionKey.isValid()) {
return;
}
readPending = true;
// 获取Channel所有感兴趣的事件
final int interestOps = selectionKey.interestOps();
// 如果ServerSocketChannel没有关注Accept事件
if ((interestOps & readInterestOp) == 0) {
// 设置其感兴趣的事件,让其关注Accepet事件
// readInterestOp 取值是 16
// 在 NioServerSocketChannel 创建时初始化
selectionKey.interestOps(interestOps | readInterestOp);
}
}
此时,获取channel关注感兴趣的事件的interestOps 因为没有关注事件所以是0,而readInterestOp是16,在SeletionKey
中定义了:
public static final int OP_ACCEPT = 1 << 4; // 即值为16
interestOps | readInterestOp
就相当于interestOps + readInterestOp
结果就是 16,也就使其关注了ACCEPT事件。