本文主要记录下我在学习Netty源代码过程中,以org.jboss.netty.example.echo.EchoServer为例,在启动Server端的时候,Netty是如何建立Boss与NIOWorker的,以及如何建立Selector、绑定服务器端口等过程的细节,借此来学习Netty的Pipeline、ChannelHandle、Boss、NioWorker等各自的分工是怎样,它们又是如何协作的?
(基础一)Netty网络模型图
(基础二)ChannelPipeline原理
具体的Netty实现原理上可以参见《Netty实现原理浅析》,该文写得很好,我这里不再详细记述。
以下为我在代码阅读的过程记述的一些要点,先就有这些东西,后续再补充完整。
第一节:建立MainReactor和SubReactor线程池
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
指定创建一个NioServerSocketChannelFactory(),建立一个BossExecutor的执行器,和一个workExecutor的执行器。
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new EchoServerHandler());
}
});
设置服务器端的ChannelPipelineFactory对象,在该工厂对象中指定使用哪一个ChannelPipeline的实现实例,在EchoServer中使用DefaultChannelPipeline来作为ChannelPipeline,同时指定ChannelPipeline中需要使用的ChannelHandle链接。 在指定ChannelPipeline的ChannelHandle的过程,对于每一个DefaultChannelPipeline的实例对象,都建立一个
第三节:建立一个Server端启动所对应的ChannelHandle链
bootstrap.bind(new InetSocketAddress(8080));
关键的动作尽在此步骤中了。首先建立了队列new LinkedBlockingQueue
第四节:打开服务器端Socket,发起一个Channel的Open的Upstream事件,后续将OPEN事件转换为一个Downstream的BIND事件
Channel channel = getFactory().newChannel(bossPipeline);
利用上述第(1)步中建立的对象NioServerSocketChannelFactory来建立一个ServerSocketChannel,在创建NioServerSocketChannel对象的时候, 会调用如下代码块:
socket = ServerSocketChannel.open();
socket.configureBlocking(false);
紧接下来就是调用fireChannelOpen(this)来调用步骤(3)中建立的DefaultChannelPipeline对象,发送一个upstream事件,ChannelState为OPEN 这个是触发upstream的ChannelHandle的第一个入口,后续就会依次调用DefaultChannelHandlerContext的Next所指向的ChannelHandle的handleUpstream()方法, 对于EchoServerHandle来说,对应的handleUpstream()方法就会调用SimpleChannelsUpstreamHandle的handleUpstream()方法,对于ChannelStateEvent.OPEN事件来说, 会调用ServerBootstrap.Binder.channelOpen()方法,在Binder这个ChannelHandle处理完成之后,又会使用DefaultChannelHandleContext.sendUpstream(evt)来处理后续的链节点, 只是这里只有一个ChannelHandle,到这里就执行完毕了。 在ServerBootstrap.Binder.channelOpen()方法中有如下代码:boolean finished = futureQueue.offer(evt.getChannel().bind(localAddress));将一个ChannelState.OPEN事件转换为 一个BIND事件了,即调用NioServerSocketChannel.bind()方法,具体的实现代码在AbstractChannel.bind()方法中,实则是调用Channels.bind()方法,其中Channels类是一个工具类。
第五节:完成DownStream的BIND事件的处理
ChannelFuture future = future(channel);
channel.getPipeline().sendDownstream(new DownstreamChannelStateEvent(
channel, future, ChannelState.BOUND, localAddress));
Channels.bind()方法中主要是如上代码,将ChannelState.BIND事件,构造为一个downstream事件,由Boss的ChannelPipeline中的ChannelHandle链来完成这个downstream的处理。 DefaultChannelPipeline.sendDownstream()方法会从ChannelHandle链的链尾节点开始找能够处理downstream的ChannelHandle节点来处理该事件,在这里由于ServerBootstrap.Binder只是一个 Upstream的Handle,所以没有对应的downstream的ChannelHandle节点可以处理该事件,直接就由(1)步所建立的NioServerSocketPipelineSink对象来完成downstream的末端处理,即会调用NioServerSocketPipelineSink.eventSunk()方法,再到NioServerSocketPipelineSink.handleServerSocket()方法,再又调用了NioServerSocketPipelineSink.bind()方法。
第六节:将服务器端Socket绑定到指定端口
channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());
NioServerSocketPipelineSink.bind()方法中有如上代码,完成关键的一步,即将建立了一个ServerSocketChannel,并且绑定到指定的端口
第七节:触发一个Upstream的BOUND事件
fireChannelBound(channel, channel.getLocalAddress());
NioServerSocketPipelineSink.bind()方法中有如上代码,触发一个Upstream的BOUND事件,但是遗憾的是后续并没有看到什么有特别用途的操作发生。
第八节:将服务器端Socket与selector绑定起来,完成对后续Client链接的监听
Executor bossExecutor =
((NioServerSocketChannelFactory) channel.getFactory()).bossExecutor;
DeadLockProofWorker.start(
bossExecutor,
new ThreadRenamingRunnable(
new Boss(channel),
"New I/O server boss #" + id + " (" + channel + ')'));
bossStarted = true;
这个是NioServerSocketPipelineSink.bind()方法中的一段代码,其中有一个new Boss(channel)操作,会执行一个Selector.open()动作,再继而以channel.socket.register(selector, SelectionKey.OP_ACCEPT);这个动作把刚才新建的NioServerSocketChannel与一个Selector的实例绑定起来。其中的DeadLockProofWorker.start()这个是将一个绑定的Boss线程放到(1)步骤中的bossExecutor线程池中,开始执行Boss线程。
第九节:BOSS线程轮询接收Client来链接请求
for (;;) {
try {
if (selector.select(1000) > 0) {
selector.selectedKeys().clear();
}
SocketChannel acceptedSocket = channel.socket.accept();
if (acceptedSocket != null) {
registerAcceptedChannel(acceptedSocket, currentThread);
}
} catch (SocketTimeoutException e) {
// Thrown every second to get ClosedChannelException
// raised.
} catch (CancelledKeyException e) {
// Raised by accept() when the server socket was closed.
} catch (ClosedSelectorException e) {
// Raised by accept() when the server socket was closed.
} catch (ClosedChannelException e) {
// Closed as requested.
break;
} catch (Throwable e) {
logger.warn(
"Failed to accept a connection.", e);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// Ignore
}
}
}
以上代码为NioServerSocketPipelineSink.Boss.run()中的一部分,主要母的就是让selector来不断的轮询,看是否有客户端的链接过来,有的话就利用registerAcceptedChannel(acceptedSocket, currentThread);来将链接来的Socket注册到NioWorker,有NioWorker(subreactor)来完成对客户端链接的处理。
至此Netty的服务端启动完成了。
对于服务端的启动过程,Netty也是以ChannelHandler链的方式来实现的,先是实现一个Upstream的OPEN事件,再变换为Downstream的BOUND事件,不知为什么原因又触发了一个第七节的Upstream的BOUND事件出来,但是该事件并没有完成什么有效的操作。这个要存疑,待后续再阅读与品味!