- 浏览: 149300 次
最新评论
-
zjhgx:
写的不错
tomcat架构分析 (Session管理) -
smallbug_vip:
very very good 如此好文章,一定要刷新一下最后评 ...
tomcat架构分析(概览) -
slightz:
楼主我已经深深的爱上你了
tomcat 性能之connector -
js_nicle:
受益匪浅,谢谢博主。
tomcat架构分析-索引 -
lianglaiyang:
楼主,什么时候给我们讲一下nio connector中的com ...
tomcat架构分析 (connector NIO 实现)
出处:http://gearever.iteye.com
上一篇简单记录了缺省配置的connector的内部构造及消息流,同时此connector也是基于BIO的实现。除了BIO外,也可以通过配置快速部署NIO的connector。在server.xml中如下配置;
整个tomcat是一个比较完善的框架体系,各个组件之间都是基于接口的实现,所以比较方便扩展和替换。像这里的“org.apache.coyote.http11.Http11NioProtocol”和BIO的“org.apache.coyote.http11.Http11Protocol”都是统一的实现org.apache.coyote.ProtocolHandler接口,所以从整体结构上来说,NIO还是与BIO的实现保持大体一致。
首先来看一下NIO connector的内部结构,箭头方向还是消息流;
还是可以看见connector中三大块
基本功能与BIO的类似,参见tomcat架构分析(connector BIO实现)。重点看看Http11NioProtocol.
和JIoEndpoint一样,NioEndpoint是Http11NioProtocol中负责接收处理socket的主要模块。但是在结构上比JIoEndpoint要复杂一些,毕竟是非阻塞的。但是需要注意的是,tomcat的NIO connector并非完全是非阻塞的,有的部分,例如接收socket,从socket中读写数据等,还是阻塞模式实现的,在后面会逐一介绍。
如图所示,NioEndpoint的主要流程;
图中Acceptor及Worker分别是以线程池形式存在,Poller是一个单线程。注意,与BIO的实现一样,缺省状态下,在server.xml中没有配置<Executor>,则以Worker线程池运行,如果配置了<Executor>,则以基于java concurrent 系列的java.util.concurrent.ThreadPoolExecutor线程池运行。
Acceptor
接收socket线程,这里虽然是基于NIO的connector,但是在接收socket方面还是传统的serverSocket.accept()方式,获得SocketChannel对象,然后封装在一个tomcat的实现类org.apache.tomcat.util.net.NioChannel对象中。然后将NioChannel对象封装在一个PollerEvent对象中,并将PollerEvent对象压入events queue里。这里是个典型的生产者-消费者模式,Acceptor与Poller线程之间通过queue通信,Acceptor是events queue的生产者,Poller是events queue的消费者。
Poller
Poller线程中维护了一个Selector对象,NIO就是基于Selector来完成逻辑的。在connector中并不止一个Selector,在socket的读写数据时,为了控制timeout也有一个Selector,在后面的BlockSelector中介绍。可以先把Poller线程中维护的这个Selector标为主Selector。
Poller是NIO实现的主要线程。首先作为events queue的消费者,从queue中取出PollerEvent对象,然后将此对象中的channel以OP_READ事件注册到主Selector中,然后主Selector执行select操作,遍历出可以读数据的socket,并从Worker线程池中拿到可用的Worker线程,然后将socket传递给Worker。整个过程是典型的NIO实现。
Worker
Worker线程拿到Poller传过来的socket后,将socket封装在SocketProcessor对象中。然后从Http11ConnectionHandler中取出Http11NioProcessor对象,从Http11NioProcessor中调用CoyoteAdapter的逻辑,跟BIO实现一样。在Worker线程中,会完成从socket中读取http request,解析成HttpServletRequest对象,分派到相应的servlet并完成逻辑,然后将response通过socket发回client。在从socket中读数据和往socket中写数据的过程,并没有像典型的非阻塞的NIO的那样,注册OP_READ或OP_WRITE事件到主Selector,而是直接通过socket完成读写,这时是阻塞完成的,但是在timeout控制上,使用了NIO的Selector机制,但是这个Selector并不是Poller线程维护的主Selector,而是BlockPoller线程中维护的Selector,称之为辅Selector。
NioSelectorPool
NioEndpoint对象中维护了一个NioSelecPool对象,这个NioSelectorPool中又维护了一个BlockPoller线程,这个线程就是基于辅Selector进行NIO的逻辑。以执行servlet后,得到response,往socket中写数据为例,最终写的过程调用NioBlockingSelector的write方法。
也就是说当socket.write()返回0时,说明网络状态不稳定,这时将socket注册OP_WRITE事件到辅Selector,由BlockPoller线程不断轮询这个辅Selector,直到发现这个socket的写状态恢复了,通过那个倒数计数器,通知Worker线程继续写socket动作。看一下BlockSelector线程的逻辑;
使用这个辅Selector主要是减少线程间的切换,同时还可减轻主Selector的负担。以上描述了NIO connector工作的主要逻辑,可以看到在设计上还是比较精巧的。NIO connector还有一块就是Comet,有时间再说吧。需要注意的是,上面从Acceptor开始,有很多对象的封装,NioChannel及其KeyAttachment,PollerEvent和SocketProcessor对象,这些不是每次都重新生成一个新的,都是NioEndpoint分别维护了它们的对象池;
当需要这些对象时,分别从它们的对象池获取,当用完后返回给相应的对象池,这样可以减少因为创建及GC对象时的性能消耗。
上一篇简单记录了缺省配置的connector的内部构造及消息流,同时此connector也是基于BIO的实现。除了BIO外,也可以通过配置快速部署NIO的connector。在server.xml中如下配置;
<Connector port="80" URIEncoding="UTF-8" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" redirectPort="7443" />
整个tomcat是一个比较完善的框架体系,各个组件之间都是基于接口的实现,所以比较方便扩展和替换。像这里的“org.apache.coyote.http11.Http11NioProtocol”和BIO的“org.apache.coyote.http11.Http11Protocol”都是统一的实现org.apache.coyote.ProtocolHandler接口,所以从整体结构上来说,NIO还是与BIO的实现保持大体一致。
首先来看一下NIO connector的内部结构,箭头方向还是消息流;
还是可以看见connector中三大块
- Http11NioProtocol
- Mapper
- CoyoteAdapter
基本功能与BIO的类似,参见tomcat架构分析(connector BIO实现)。重点看看Http11NioProtocol.
和JIoEndpoint一样,NioEndpoint是Http11NioProtocol中负责接收处理socket的主要模块。但是在结构上比JIoEndpoint要复杂一些,毕竟是非阻塞的。但是需要注意的是,tomcat的NIO connector并非完全是非阻塞的,有的部分,例如接收socket,从socket中读写数据等,还是阻塞模式实现的,在后面会逐一介绍。
如图所示,NioEndpoint的主要流程;
图中Acceptor及Worker分别是以线程池形式存在,Poller是一个单线程。注意,与BIO的实现一样,缺省状态下,在server.xml中没有配置<Executor>,则以Worker线程池运行,如果配置了<Executor>,则以基于java concurrent 系列的java.util.concurrent.ThreadPoolExecutor线程池运行。
Acceptor
接收socket线程,这里虽然是基于NIO的connector,但是在接收socket方面还是传统的serverSocket.accept()方式,获得SocketChannel对象,然后封装在一个tomcat的实现类org.apache.tomcat.util.net.NioChannel对象中。然后将NioChannel对象封装在一个PollerEvent对象中,并将PollerEvent对象压入events queue里。这里是个典型的生产者-消费者模式,Acceptor与Poller线程之间通过queue通信,Acceptor是events queue的生产者,Poller是events queue的消费者。
Poller
Poller线程中维护了一个Selector对象,NIO就是基于Selector来完成逻辑的。在connector中并不止一个Selector,在socket的读写数据时,为了控制timeout也有一个Selector,在后面的BlockSelector中介绍。可以先把Poller线程中维护的这个Selector标为主Selector。
Poller是NIO实现的主要线程。首先作为events queue的消费者,从queue中取出PollerEvent对象,然后将此对象中的channel以OP_READ事件注册到主Selector中,然后主Selector执行select操作,遍历出可以读数据的socket,并从Worker线程池中拿到可用的Worker线程,然后将socket传递给Worker。整个过程是典型的NIO实现。
Worker
Worker线程拿到Poller传过来的socket后,将socket封装在SocketProcessor对象中。然后从Http11ConnectionHandler中取出Http11NioProcessor对象,从Http11NioProcessor中调用CoyoteAdapter的逻辑,跟BIO实现一样。在Worker线程中,会完成从socket中读取http request,解析成HttpServletRequest对象,分派到相应的servlet并完成逻辑,然后将response通过socket发回client。在从socket中读数据和往socket中写数据的过程,并没有像典型的非阻塞的NIO的那样,注册OP_READ或OP_WRITE事件到主Selector,而是直接通过socket完成读写,这时是阻塞完成的,但是在timeout控制上,使用了NIO的Selector机制,但是这个Selector并不是Poller线程维护的主Selector,而是BlockPoller线程中维护的Selector,称之为辅Selector。
NioSelectorPool
NioEndpoint对象中维护了一个NioSelecPool对象,这个NioSelectorPool中又维护了一个BlockPoller线程,这个线程就是基于辅Selector进行NIO的逻辑。以执行servlet后,得到response,往socket中写数据为例,最终写的过程调用NioBlockingSelector的write方法。
public int write(ByteBuffer buf, NioChannel socket, long writeTimeout,MutableInteger lastWrite) throws IOException { SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector()); if ( key == null ) throw new IOException("Key no longer registered"); KeyAttachment att = (KeyAttachment) key.attachment(); int written = 0; boolean timedout = false; int keycount = 1; //assume we can write long time = System.currentTimeMillis(); //start the timeout timer try { while ( (!timedout) && buf.hasRemaining()) { if (keycount > 0) { //only write if we were registered for a write //直接往socket中写数据 int cnt = socket.write(buf); //write the data lastWrite.set(cnt); if (cnt == -1) throw new EOFException(); written += cnt; //写数据成功,直接进入下一次循环,继续写 if (cnt > 0) { time = System.currentTimeMillis(); //reset our timeout timer continue; //we successfully wrote, try again without a selector } } //如果写数据返回值cnt等于0,通常是网络不稳定造成的写数据失败 try { //开始一个倒数计数器 if ( att.getWriteLatch()==null || att.getWriteLatch().getCount()==0) att.startWriteLatch(1); //将socket注册到辅Selector,这里poller就是BlockSelector线程 poller.add(att,SelectionKey.OP_WRITE); //阻塞,直至超时时间唤醒,或者在还没有达到超时时间,在BlockSelector中唤醒 att.awaitWriteLatch(writeTimeout,TimeUnit.MILLISECONDS); }catch (InterruptedException ignore) { Thread.interrupted(); } if ( att.getWriteLatch()!=null && att.getWriteLatch().getCount()> 0) { keycount = 0; }else { //还没超时就唤醒,说明网络状态恢复,继续下一次循环,完成写socket keycount = 1; att.resetWriteLatch(); } if (writeTimeout > 0 && (keycount == 0)) timedout = (System.currentTimeMillis() - time) >= writeTimeout; } //while if (timedout) throw new SocketTimeoutException(); } finally { poller.remove(att,SelectionKey.OP_WRITE); if (timedout && key != null) { poller.cancelKey(socket, key); } } return written; }
也就是说当socket.write()返回0时,说明网络状态不稳定,这时将socket注册OP_WRITE事件到辅Selector,由BlockPoller线程不断轮询这个辅Selector,直到发现这个socket的写状态恢复了,通过那个倒数计数器,通知Worker线程继续写socket动作。看一下BlockSelector线程的逻辑;
public void run() { while (run) { try { ...... Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null; while (run && iterator != null && iterator.hasNext()) { SelectionKey sk = (SelectionKey) iterator.next(); KeyAttachment attachment = (KeyAttachment)sk.attachment(); try { attachment.access(); iterator.remove(); ; sk.interestOps(sk.interestOps() & (~sk.readyOps())); if ( sk.isReadable() ) { countDown(attachment.getReadLatch()); } //发现socket可写状态恢复,将倒数计数器置位,通知Worker线程继续 if (sk.isWritable()) { countDown(attachment.getWriteLatch()); } }catch (CancelledKeyException ckx) { if (sk!=null) sk.cancel(); countDown(attachment.getReadLatch()); countDown(attachment.getWriteLatch()); } }//while }catch ( Throwable t ) { log.error("",t); } } events.clear(); try { selector.selectNow();//cancel all remaining keys }catch( Exception ignore ) { if (log.isDebugEnabled())log.debug("",ignore); } }
使用这个辅Selector主要是减少线程间的切换,同时还可减轻主Selector的负担。以上描述了NIO connector工作的主要逻辑,可以看到在设计上还是比较精巧的。NIO connector还有一块就是Comet,有时间再说吧。需要注意的是,上面从Acceptor开始,有很多对象的封装,NioChannel及其KeyAttachment,PollerEvent和SocketProcessor对象,这些不是每次都重新生成一个新的,都是NioEndpoint分别维护了它们的对象池;
ConcurrentLinkedQueue<SocketProcessor> processorCache = new ConcurrentLinkedQueue<SocketProcessor>() ConcurrentLinkedQueue<KeyAttachment> keyCache = new ConcurrentLinkedQueue<KeyAttachment>() ConcurrentLinkedQueue<PollerEvent> eventCache = new ConcurrentLinkedQueue<PollerEvent>() ConcurrentLinkedQueue<NioChannel> nioChannels = new ConcurrentLinkedQueue<NioChannel>()
当需要这些对象时,分别从它们的对象池获取,当用完后返回给相应的对象池,这样可以减少因为创建及GC对象时的性能消耗。
发表评论
-
mina架构分析 (NIO 网络接口)
2013-04-10 23:39 6283出处:http://gearever.iteye.com A ... -
tomcat架构分析(connector BIO 实现)
2013-04-03 17:41 12813出处:http://gearever.iteye. ... -
tomcat架构分析 (JNDI体系绑定)
2012-06-13 16:42 8707出处:http://gearever.iteye. ... -
tomcat架构分析 (JNDI配置)
2012-06-07 11:46 7788出处:http://gearever.iteye.com J ... -
tomcat架构分析 (Session管理)
2012-06-01 12:34 20583出处:http://gearever.iteye.com S ... -
tomcat架构分析-索引
2012-05-30 21:26 7398出处:http://gearever.iteye.com ... -
tomcat 性能之connector
2012-05-30 20:55 2721今天重新分析了一下tomcat的connector接口的阻塞模 ... -
tomcat架构分析(valve源码导读)
2012-05-24 19:17 5984出处:http://gearever.iteye.com 源 ... -
tomcat架构分析(valve机制)
2012-05-21 10:00 16764出处:http://gearever.iteye. ... -
tomcat架构分析(容器类)
2012-05-17 14:32 14952出处:http://gearever.itey ... -
tomcat架构分析(概览)
2012-05-16 16:23 16454出处:http://gearever.iteye.com T ... -
重新开始
2012-05-16 16:04 143之前架了个blog在GAE上,但是不是很稳定,GAE时常被盾, ...
相关推荐
《Tomcat架构解析与优化》一书主要涵盖了Apache Tomcat服务器的基础架构、工作原理以及性能调优策略。Tomcat作为一款广泛使用的Java Servlet容器,它的高效运行和优化对于任何Java Web应用都至关重要。 首先,...
以下是关于Tomcat架构的详细分析。 首先,核心架构模块是Tomcat的基础,包括Server、Service、Engine、Host和Context以及Wrapper等。Server作为最顶层的组件,包含了多个Service。Service是Tomcat对外提供服务的...
例如,Tomcat提供了基于NIO(非阻塞I/O)和BIO(同步阻塞I/O)的不同Connector实现。 Container进一步细分为多个层次,包括Host、Context、Wrapper等,每个层次负责处理不同粒度的Web应用。Engine是最高级别的...
**Tomcat架构解析** Tomcat是一款广泛使用的开源Java Servlet容器,由Apache软件基金会开发和维护。它是Java EE(现在称为Jakarta EE)平台的核心部分,主要用于托管Web应用程序。本篇文章将深入探讨Tomcat的架构,...
《Tomcat架构解析》是由刘光瑞编著的一本深入探讨Tomcat服务器核心架构的专业书籍。这本书详尽地阐述了Tomcat的内部工作机制,旨在帮助读者理解如何优化和管理这个广泛应用的Java Servlet容器。以下是对Tomcat架构的...
**Tomcat架构解析** Tomcat是一款广泛使用的开源Java Servlet容器,它是Apache软件基金会下的Jakarta项目的一部分。Tomcat实现了Java Servlet和JavaServer Pages(JSP)规范,为Web应用程序提供了运行环境。本篇将...
一、Tomcat架构概述 Tomcat基于Servlet和JavaServer Pages(JSP)标准,其架构主要由以下几个组件构成: 1. **Catalina**:这是Tomcat的核心,负责处理Servlet规范中的请求和响应。Catalina提供了Servlet容器,...
`Server`接口是Tomcat架构的顶级组件,它代表了一个完整的Tomcat实例。一个`Server`可以包含多个`Service`,每个`Service`都有自己的网络监听端口,这样在一个`Server`中就可以配置多个不同的服务,每个服务监听...
总结来说,“简单的tomcat实现1”主要涉及了Tomcat的基础架构和工作流程,包括连接器的实现、容器的构建、请求处理逻辑以及Web应用的部署和Servlet的生命周期管理。通过模拟这些关键部分,读者可以更好地理解Tomcat...
二、Tomcat架构解析 1. 启动流程:Tomcat启动时,会加载server.xml配置文件,初始化各个组件,如Connector(网络连接器)、Executor(线程池)、Context(应用程序上下文)等。 2. 工作线程模型:Tomcat使用Coyote ...
4. **连接器(Connector)与引擎(Engine)**:Tomcat的核心架构包括Connector和Container,其中Connector负责接收和发送HTTP请求,Container负责处理请求。在`conf/server.xml`中,你可以配置这些组件。 5. **部署...
- **解析器实现分析**: - 前缀模式匹配:通过匹配XML标签的前缀来确定对应的处理逻辑。 - 基于规则:根据预定义的规则集合来处理XML元素。例如,当遇到`<Server>`元素时,会创建一个`StandardServer`实例,并将...
一、Tomcat的架构与工作原理 Tomcat基于Java语言实现,遵循Java EE的Web应用规范。它主要由以下几个核心组件构成: 1. Connector:负责接收HTTP请求并将其转发给相应的Servlet容器处理。 2. Container:包含了...
Tomcat 的 Connector 运行模式有 bio、nio 和 apr 三种: * bio 模式:传统的 Java I/O 操作,同步且阻塞 IO。 * nio 模式:JDK1.4 开始支持,同步阻塞或同步非阻塞 IO。nio 模式可以提高服务器的性能和可扩展性。 ...
- **整体架构**:介绍了Tomcat的基本架构,包括Server、Service、Connector和Container等组件。 - **工作原理**:深入剖析了Tomcat如何处理客户端请求的过程,包括请求接收、处理和响应生成。 #### 1.13《How ...
1. **Tomcat架构**:Tomcat基于Servlet容器的概念,它主要由Catalina、Apr、Jasper、Juli、JMX Remote等组件构成。Catalina是核心,负责处理Servlet请求;Apr提供高性能的网络连接;Jasper解析JSP文件为Servlet源...
一、Tomcat架构与工作原理 Tomcat的架构基于Java Servlet规范,主要由Catalina核心、Jasper JSP引擎、 Coyote HTTP/1.1连接器和Cluster模块等组成。Catalina负责处理Servlet容器的主要功能,Jasper解析并编译JSP页面...
Coyote主要关注网络I/O部分,为Tomcat提供了高效的连接器(Connector)实现,包括基于NIO(非阻塞I/O)和APR(Apache Portable Runtime)的实现。 Coyote的HTTP连接器负责接收来自客户端的HTTP请求,并将这些请求...
9. **性能优化**:Tomcat可以通过调整各种参数进行性能优化,如增大最大连接数、调整线程池大小、启用NIO模式等。理解这些优化策略有助于提升服务器的承载能力。 10. **故障排查与日志**:Tomcat的日志系统提供了...