见:
http://blog.csdn.net/zhangzhaokun/article/details/6854494
本文主要记录下我在学习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的执行器。
同时创建一个NioServerSocketPipelineSink(workerExecutor,workCount)对象,建立cup*2个NioWorker的实例,
这个类主要在于实现downstream已经达到末端的时候的处理事件,简单来说就是downstream的最后一个处理环节了。
第二节:完成Client请求的ChannelHandle链的建立
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new EchoServerHandler());
}
});
设置服务器端的ChannelPipelineFactory对象,在该工厂对象中指定使用哪一个ChannelPipeline的实现实例,在EchoServer中使用DefaultChannelPipeline来作为ChannelPipeline,同时指定ChannelPipeline中需要使用的ChannelHandle链接。 在指定ChannelPipeline的ChannelHandle的过程,对于每一个DefaultChannelPipeline的实例对象,都建立一个<name,ChannelHandle>的映射关系, 并且对于每一个ChannelHandle都关联一个DefaultChannelHandleContext对象,在该对象中指定了当前ChannelHandle的前一个、后一个ChannelHandle, ChannelPipeline中的DefaultChannelHandleContext是按照ChannelHandle1(Head:prev=null,next=ChannelHandle2)->ChannelHandle1(tail:prev=ChannelHandle1,next=null)构造ChannelHandle链的,对于downstream的事件,是从tail->head的顺序执行ChannelHandle的,所以prev为空表示downstream链的末端,next为空表示upstream链的末端,在downstream链的末端则需要执行NioServerSocketPipelineSink来处理了。
第三节:建立一个Server端启动所对应的ChannelHandle链
bootstrap.bind(new InetSocketAddress(8080));
关键的动作尽在此步骤中了。首先建立了队列new LinkedBlockingQueue<ChannelFuture>(),再次之后就建立了一个处理Boss的ChannelHandle实例对象Binder(), 之后再建立了一个DefaultChannelPipeline对象来作为Boss的ChannelPipeline,该ChannelPipeline中的ChannelHandle对象就只有一个Binder()的ChannelHandle了,并且该Binder对象实例只是一个处理upstream的ChannelHandle
第四节:打开服务器端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事件出来,但是该事件并没有完成什么有效的操作。这个要存疑,待后续再阅读与品味!
分享到:
相关推荐
Bootstrap是启动Netty应用的基础类,通过它我们可以设置各种参数,如连接地址、线程组、通道类型等。一个典型的Bootstrap配置示例如下: ```java EventLoopGroup bossGroup = new NioEventLoopGroup(); ...
Netty是一款高性能的网络应用程序框架,它使用Java编程语言开发,主要用于网络应用程序的快速和易于开发,支持TCP和UDP...通过对Netty源码的深入分析,可以更好地理解其工作机制,对开发高性能的网络应用有极大的帮助。
源码分析时,首先需要关注的是 Netty 的启动流程,这通常从 `ServerBootstrap` 类开始。ServerBootstrap 配置了 EventLoopGroup(包含多个 EventLoop)和 Channel 实例,如 NioServerSocketChannel。然后通过绑定...
在提供的资源 "课时07:NettyServerBootstrap启动的详细过程类图分析.avi" 中,可能包含了动画或讲解视频,详细展示了这些步骤的可视化过程,帮助理解NettyServerBootstrap启动时涉及的类和对象之间的关系,以及它们...
本文档主要讲述了Netty5.0架构剖析和源码解读,涵盖了Netty的架构、源码分析、NIO入门等方面的知识点。 概述 JAVA 的 IO 演进是一个长期的过程,从传统的 BIO 通信到 NIO 的出现,都是为了解决通信中的问题。传统...
本书中的案例涵盖了Netty的启动和停止、内存、并发多线程、性能、可靠性、安全等方面,囊括了Netty绝大多数常用的功能及容易让人犯错的地方。在案例的分析过程中,还穿插讲解了Netty的问题定位思路、方法、技巧,...
在本系列文章中,作者也会结合代码分析来阐述Netty的各个组成部分,以便于读者能够更清晰地了解Netty的运作机制。作者欢迎任何形式的反馈,并且会根据读者的建议对文章内容进行更新和完善。同时,读者可以通过GitHub...
9. **实例代码**:通常会包含如何配置和启动一个支持长连接的Netty服务器,以及客户端如何连接和发送数据的示例代码。 由于没有具体的博客内容,以上是基于常见情况的分析。实际的博客可能会详细解释这些概念,并...
### Java Netty 异常分析:BindException 在开发基于Java Netty的应用程序时,可能会遇到`java.net.BindException: Address already in use: no further information`这类错误。本篇文章将详细解析这一异常的原因、...
《Netty进阶之路:跟着案例学Netty》中的案例涵盖了Netty的启动和停止、内存、并发多线程、性能、可靠性、安全等方面,囊括了Netty绝大多数常用的功能及容易让人犯错的地方。在案例的分析过程中,还穿插讲解了Netty...
最后,书中还包含了一些高级主题,如Netty的性能调优技巧、安全特性(如SSL/TLS加密)以及实战案例分析,帮助读者在实际项目中更好地应用Netty。 总的来说,《Netty权威指南》是一本全面且深入的Netty学习资料,...
Netty 是一个高性能、异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议...通过 TimeServer 和 TimeClient 示例,我们可以逐步掌握 Netty 的基本用法,并为进一步探索其高级特性和源码分析打下基础。
通过分析和运行这个示例,你可以更深入地理解如何将Netty和protobuf整合在一起,实现高效的数据通信。 总之,Netty和protobuf的结合为开发者提供了一种强大的网络通信解决方案。通过protobuf,我们可以定义清晰的...
4. **启动Netty服务器**:在Spring的启动类或者配置类中,使用Spring注入的Netty服务器配置启动服务器,监听指定端口。 5. **客户端交互**:创建Netty客户端,同样利用Spring管理的bean进行通信,也可以使用Spring...
3. **ServerBootstrap类**:这是Netty中启动服务器的主要入口点。在API接口文档中,你会看到如何配置ServerBootstrap以设置各种参数,如线程池、管道工厂等。 4. **Pipeline和Handler**:Netty使用Pipeline来处理...
1. **Netty 服务器启动与绑定端口** 当我们创建一个 Netty 服务器时,首先需要定义一个 `ServerBootstrap` 对象。这个对象负责配置服务器的行为,包括选择使用的线程模型(NIO 或 Epoll),处理入站和出站事件的 `...
- **Bootstrap**:是Netty应用启动引导类,用于初始化并启动Netty服务。 - **Channel**:是所有I/O操作的基础,可以理解为连接的抽象表示。 - **EventLoop**:负责处理I/O事件,包括注册、读取、写入等操作。 - **...
NettyChannel是一个基于Netty框架的测试程序,它...通过分析和运行`NettyServer`和相应的客户端程序,可以深入了解Channel在实际操作中的行为,包括连接建立、数据交换和关闭流程,有助于开发者更好地掌握Netty框架。