以部署web.war到tomcat的webapps目录为例,其暴露了1个rest服务,服务路径为/app/demo。在IDEA中调试tomcat源码,源码的目录如下:
tomcat7整体架构图如下:
- 1个Server可以包含多个Service,1个Service包含1个容器Container和多个连接器Connector。
- Container包含4种类型,从大到小分别为Engine、Host、Context、Wrapper。Engine可以包含多个Host子容器,1个Host可以包含多个Context子容器,1个Context可以包含多个Wrapper子容器。
- 以请求localhost:8080/web/app/demo为例,格式为{host}:port/{app}/{path},{host}部分对应到Host容器,{app}部分对应到Context容器,即1个应用,{path}对应到Wrapper容器。通过war包部署到tomcat中后,{app}就是war包的文件名(去掉.war)。
- 对于本例,Host容器包含5个Context容器,分别是StandardContext("")、StandardContext("/docs")、StandardContext("/host-manager")、StandardContext("/manager")和StandardContext("/web"),分别对应webapps目录下的ROOT、docs、host-manager、manager和web目录对应的应用,除了web目录,其他几个目录都是tomcat原有的。
- 1个Context容器对应1个应用,其包含1个WebappClassLoader用于加载该应用独有的类,实现不同应用间的隔离。对于本例,StandardContext("/web")包含3个子容器,分别是StandardWrapper(default)、StandardWrapper(jsp)和StandardWrapper(dispatcher)。
- 1个Wrapper对应1个Servlet,在本例中,3个Wrapper容器对应3个servlet,分别为DefaultServlet、JspServlet和DispatcherServlet。
- Connector包含Acceptor和Http11Processor两个组件,Acceptor用于接收请求,Http11Processor用于处理请求,Acceptor在独立的线程中循环执行接收客户端请求,Http11Processor在异步线程中处理请求,触发顶级容器Engine的pipeline,pipeline由1个或多个valve组成,接下来请求会在Engine的pipeline的valve中流动处理,在Engine的最后1个valve(StandardEngineValve)中会触发其子容器Host的pipeline,依次类推,每中类型的容器在最后1个valve中都会触发子容器的pipeline,最终传到Wrapper的StandardWrapperValve中,通过其父容器Context的WebappClassLoader加载对应的servlet处理类,创建servlet并处理请求。
- StandardEngine默认只有1个valve:StandardEngineValve,执行的操作是将请求传入到StandardHost的pipeline;StandardHost默认有2个valve:ErrorReportValve和StandardHostValve,StandardHostValve执行的操作是将请求传入到StandardContext的pipeline;StandardContext默认只有1个valve:StandardContextValve,执行的操作是将请求传入到StandardWrapper的pipeline;StandardWrapper默认只有1个valve:StandardWrapperValve,执行的操作是通过父容器StandardContext的WebappClassLoader加载servlet类并创建servlet对象,执行servlet.service(request,response)处理请求。
一次调用请求的调用链如下:
对于servlet的load过程,分为两种情况:
- 如果servlet实例实现了SingleThreadModel接口,则在servlet池中获取,如果池中没有可用servlet实例则创建一个servlet并放入池中,如果池中有可用servlet实例则pop出一个返回。如果当前servlet实例数量超过了阈值,那么wait等待可用servlet不能再继续创建。
- 如果servlet实例没有实现SingleThreadModel接口,则共享一个servlet,直接返回。