Tomcat架构分析及性能调优

一.Tomcat容器层级结构
分为四个等级,由四个子容器组成,Container容器:Engine->Host->Servlet容器->多个Context->多个Wrapper。真正管理Servlet的容器是Context容器,一个Context对应一个Web工程,Context直接管理Servlet在容器中的包装类Wrapper。

二.Servlet容器的启动过程
添加一个Web应用时将会创建一个StandardContext容器,并且给这个Context容器设置必要的参数,如url和path,最后将这个Context容器加到父容器Host中。接下来将会调用Tomcat的start方法启动Tomcat,它的启动逻辑是基于观察者模式设计的,所有的容器都会继承Lifecycle接口,它管理着容器的整个生命周期,所有容器的修改和状态的改变都会由它去通知已经注册的观察者(Listener)。
下面1-5介绍了Servlet的解析过程:
1.StandardContext的init方法
添加到Context容器的Listener将会被调用,ContextConfig继承了LifecycleListener接口,它是在调用Tamcat.addWebapp被加入到StandardContext容器中的。ContextConfig类会负责整个Web应用的配置文件的解析工作。
2.ContextConfig的init方法
(1)创建用于解析XML配置文件的contextDigester对象。
(2)读取默认context.xml配置文件,如果存在解析它。
(3)读取默认Host配置文件,如果存在解析它。
(4)读取默认Context自身的配置文件,如果存在解析它。
(5)设置Context的DocBase。
3.ContextConfig的startInternal方法
(1)创建读取资源文件的对象。
(2)创建ClassLoader对象。
(3)设置应用的工作目录。
(4)启动相关的辅助类,如logger、realm、resources等。
(5)修改启动状态,通知感兴趣的观察者(Web应用的配置)。
(6)子容器的初始化。
(7)获取ServletContext并设置必要的参数。
(8)初始化"load on startup"的Servlet。
4.ContextConfig的configureStart方法
Web应用的初始化,主要是解析Web.xml文件。Tomcat首先会找到globalWebXml,这个文件的搜索路径是engine的工作目录下的NO_DEFAULT_XML或conf/web.xml。接着会找hostWebXml,然后寻找应用的配置文件WEB-INF/web.xml。web.xml文件中的各个配置项将会被解析成相应的属性保存在WebXml对象中。如果当前应用支持Servlet3.0,解析还将完成额外9项工作,包括:annotations的支持等。
5.WebXml的configureContext方法
会将WebXml对象中的属性设置到Context容器中,这里包括创建的Servlet对象包装成StandardWrapper、filter、listener等。

三.创建Servlet实例
1.创建Servlet对象
如果Servlet的load-on-startup配置项大于0,那么在Context容器启动的时候就会被实例化,在解析配置文件时会读取默认的globalWebXml,在conf下的web.xml文件中定义了一些默认的配置项,其中定义了两个Servlet,分别是DefaultServlet和JspServlet。它们的load-on-startup分别是1和3,当Tomcat启动时这两个Servlet就会被启动。
创建Servlet实例的方法是从Wrapper.loadServlet开始。loadServlet方法要完成的就是获取servletClass,然后把它交给InstanceManager去创建一个基于servletClass.class的对象。如果这个Servlet配置了jsp-file,那么这个servletClass就是conf/web.xml中定义的JspServlet了。
2.初始化Servlet
初始化Servlet在StandardWrapper的initServlet方法中,调用Servlet的init()方法,同时把包装了StandardWrapper对象的StandardWrapperFacade作为ServletConfig传给Servlet。

四.Servlet体系结构
Servlet规范就是基于这几个类运转的,与Servlet主动关联的是三个类,分别是ServletConfig、ServletRequest和ServletResponse。其中ServletConfig在Servlet初始化时就传递给Servlet了,而后两个是在请求达到时调用Servlet传递过来的。StandardWrapper和StandardWrapperFacade都实现了ServletConfig接口,而StandardWrapperFacade是StandardWrapper门面类。

五.Servlet如何工作
用户从浏览器向服务器发起一个请求通常会包含如下信息:http://hostname:port/contextpath/servletpath,hostname和port用来与服务器建立TCP连接,而后面的URL才用来选择服务器中哪个子容器服务用户的请求。
(1)映射工作由专门的一个类来完成,这个类就是org.apache.tomcat.util.http.Mapper,这个类保存了Tomcat的Container容器中的所有子容器的信息,Request类在进入Container容器之前,Mapper将会根据这次请求的hostname和contextpath将host和context容器设置到Request的mappingData属性中,来确定要访问具体的子容器。然后初始化MapperListener,但请求到达最终的Servlet必须要执行Filter链,以及要通知在web.xml中定义的listener。
(2)执行Servlet的service方法,可以通过继承HttpServlet覆盖相应的方法来实现,MVC框架的基本原理是就是将所有的请求都映射到一个Servlet,然后去实现service方法,这个方法是框架入口,生命周期结束调用destroy方法。

六.Servlet中的Listener
Servlet中提供了6种两类事件的观察者接口:
1.EventListeners类型
ServletContextAttributeListener、ServletRequestAttributeListener、ServletRequestListener、HttpSessionAttributeListener。
2.LifecycleListeners类型
ServletContextListener、HttpSessionListener。

七.Servlet中的Filter
FilterConfig和FilterChain的实现类分别是ApplicationFilterConfig和ApplicationFilterChain,而Filter的实现类由用户自定义。
Filter类中的三个接口方法:
1.init(FilterConfig):Filter初始化时被调用,可以获得ServletContext对象,还能获取在<filter>下配置的<init-param>参数值。
2.doFilter(ServletRequest, ServletResponse, FilterChain):每个用户的请求都会调用这个方法,在Servlet的service方法之前被调用。通过调用FilterChain.doFilter可以将请求继续传递下去,反之就拦截了这个请求直接返回,这是一种责任链设计模式。
3.destroy:当Filter对象被销毁时,这个方法被调用。注意,当Web容器调用这个方法之后,容器会在调用一次doFilter方法。
所有的符合请求地址的Filter对象都保存在ApplicationFilterChain对象的Filters数组中。当FilterChain上所有的Filter对象执行完成后,就会执行最终的Servlet。所以ApplicationFilterChain对象中会持有Servlet对象的引用。Filter的匹配规则在ApplicationFilterFactory.matchFiltersURL方法中定义,只要匹配成功,这些Filter都会在请求链上被调用。

八.Servlet中的url-pattern
Servlet中是通过Mapper类完成的,这个类会根据请求的URL来匹配每个Servlet中配置的<url-pattern>,所以它在一个请求被创建时就已经匹配了。如果没有匹配到所有的Servlet,就会直接返回,不会调用任何Filter对象了。
web.xml加载时,检查<url-pattern>配置是否符合规则,这个检查是在StandardContext的validateURLPattern方法中检查的。
三种解析规则:
1.精确匹配:如/foo.htm
2.路径匹配:如/foo/*
3.后缀匹配:如*.htm
Servlet的匹配规则在Mapper.internalMapWrapper中定义,出现多个符合时,匹配优先级为精确匹配>路径匹配>后缀匹配。

九.Tomcat总体结构
Tomcat的心脏是两个组件:Connector和Container,Connector组件是可以被替换的,一个Container可以选择对应多个Connector。多个Connector和一个Container就形成了一个Service。有了Service就可以对外提供服务了。Service的生存环境是由Server提供,所以整个Tomcat的生命周期由Server控制。
1.Service
Tomcat中Service接口的标准实现类是StandardService,它不仅实现了Service接口,同时还实现了Lifecycle接口,这样它就可以控制它下面的组件的生命周期了。
2.Server
Server任务就是提供一个接口让其他程序能够访问到这个Service集合,同时要维护它所包含的所有Service的生命周期,包括如何初始化、如何结束服务、如何找到别人要访问的Service。它的标准实现类StandardServer实现了Lifecycle、MbeanRegistration两个接口的所有方法。
3.Lifecycle
Tomcat中组件的生命周期是通过Lifecycle接口来控制的,Server的Start方法就会循环调用所有Service组件的Start方法,但是所有Service必须要实现Lifecycle接口。

十.Connector
主要任务是负责接收浏览器发过来的TCP连接请求,创建一个Request和Response对象分别用于和请求端交换数据。然后会产生一个线程来处理这个请求并把产生的Request和Response对象传给处理这个请求的线程,处理这个请求的线程就是Container组件要做的事了。

十一.Container
Container是容器的父接口,所有子容器都必须实现这个借口,Container容器的设计用的是典型的责任链的设计模式,它由四个子容器组件构成,分别是Engine、Host、Context和Wrapper。
1.容器的总体设计
Context还可以定义在父容器Host中,Host不是必须的,但是要运行war程序,就必须要用到Host,因为war中必有web.xml文件,这个文件的解析就需要Host。如果要有多个Host就要定义一个top容器Engine。而Engine没有父容器了,一个Engine代表一个完整的Servlet引擎。
2.Engine容器
定义了一些基本的关联关系,它的标准实现类是StandardEngine,Engine没有父容器了,子容器也只能是Host类型的。
3.Host容器
Host是Engine的子容器,一个Host在Engine中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它的子容器通常是Context,它除了关联子容器外,还有就是保存一个主机应该有的信息。
4.Context容器
Context代表Servlet的Context,它具备了Servlet运行的基本环节,理论上只要有Context就能运行Servlet了。简单的Tomcat可以没有Engine和Host。
Context最重要的功能就是管理它里面的Servlet实例,Servlet实例在Context中是以Wrapper出现的。获取子容器(找到正确的Servlet)都是通过Request来分配的。
5.Wrapper容器
Wrapper代表一个Servlet,它负责管理一个Servlet,包括Servlet的装载、初始化、执行及资源回收。Wrapper是最底层的容器,它没有子容器了。
装载了Servlet后就会调用Servlet的init方法,同时会传一个StandardWrapperFacade对象给Servlet。

十二.Tomcat中的设计模式
Tomcat中用了很多设计模式,如模板模式、工厂模式、单例模式、门面模式(如Request对象的封装)、观察者模式(如Lifecycle)、命令模式(如Connector)和责任链模式(如Engine)。

十三.Tomcat与Jetty的比较
1.架构:Jetty更简单,很多需要功能需要自己实现,也更容易被扩展,而Tomcat臃肿很多,已实现的功能也会多一些。
2.性能:Tomcat处理少数非常繁忙的连接更有优势,也就是说连接的生命周期如果短,Tomcat性能更高,默认使用的是BIO。Jetty相反,它可以同时处理大量连接而且可以长时间保持这些连接。如:Web聊天。默认使用的是NIO技术,在处理请求上更占优势。
3.特性:Tomcat使用更加广泛,它对这些支持得更加全面一些,有很多特性Tomcat都直接集成进来了。Jetty的修改更加简单,它只要把相应的组件替换就好了。

十四.tomcat性能调优
1.设置最大线程数(HTTP通道调优)
2.设置gizp压缩(HTTP通道调优)
3.禁用 DNS查询
4.调整虚拟内存

猜你喜欢

转载自mengqingyu.iteye.com/blog/2017797