阅读 154

Tomcat9的容器

Tomcat中有四个容器,分别是Engine,Host,Context,Wrapper。 它们之间是逐层包含的父子关系。Context和Wrapper是“动态添加的”,在站点目录下每放置一个war包,就会动态添加一个Context,在web.xml里每配置一个servlet,就可以动态添加一个Wrapper。


Engine没有父容器, 调用 setParent方法时将会报错,添加子容器也只能是 Host 类型的。一个 Engine代表一个完整的 Servlet 引擎,接收来自Connector的请求,并决定传给哪个Host来处理。

Host容器是Engine的子容器,一个Host在Engine中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它负责安装和展开这些应用 ,并且标识这个应用以便能够区分它们。每个虚拟主机对应一个域名,不同Host容器接受处理不同域名的请求。

Context表示一个Web应用程序,它代表Servlet的Context,它具备了Servlet运行的基本环境。Tomcat的世界里,一个Host容器代表一个虚拟机资源,Context容器代表一个应用,所谓的部署器就是能够把Context容器添加进Host容器中去的一个组件。Context的配置文件中有个reloadable属性,当这个reloadable设为true时,war被修改后Tomcat会自动重新加载这个应用,在 ContainerBase.backgroundProcess()实现。

Context context = new StandardContext(); 

Host host = new StandardHost(); 

host.addChild(context);  

Wrapper表示一个Servlet。它负责管理一个Servlet,包括Servlet的装载、初始化、执行以及资源回收。Wrapper是最底层的容器,它没有子容器了,所以调用它的addChild将会报错。Wrapper的实现类是StandardWrapper,StandardWrapper还实现了拥有一个Servlet初始化信息的ServletConfig。Wrapper是对一个Servlet的包装,所以它的基础阀内部调用的过滤器链的doFilter方法和Servlet的service方法。


方法 描述
init 在第一次请求该Servlet(默认)或容器启动时,Servlet容器就会调用init(),且只调用一次
service 每次请求Servlet都会调用该方法
destroy 销毁Servlet时(卸载应用/关闭容器时),调用该方法


StandardEngine、StandardContext、StandardHost、StandardWrapper都继承 了ContainerBase类, ContainerBase内部类ContainerBackgroundProcessor 会周期的执行 run(), 它是运行在一个后台线程中, run()会周期调用所有容器的 backgroundProcess 方法

ContainerBase.backgroundProcess()方法:

  • cluster

Catalina的集群组件,通过cluster组件可以实现集群应用部署,多个Tomcat实例,不需要每个都分别部署应用,只需要在某个实例上部署,整个集群中的各个实例都会自动同步应用进行部署。 它监听指定文件夹下有没有新增的war包或者文件是新增的还是修改的已决定来重新部署和通知其他tomcat集群成员

  • loader
 Catalina在启动过程中创建的classloader的实例, 查看Context容器是否需要重新加载,热部署就是利用这个机制来完成的

  • manager

 Catalina中的Session管理器, 主要的功能是将过期会话(session)置为无效

  •  realm
 Catalina中的安全组件,可以用来管理资源的访问权限

  •  pipeline
遍历并调用容器pipeline的valve链表的backgroundProcess()


 ContainerBase.startInternal()方法:

  • Cluster.start()、Realm.start()
  • for循环对每个子容器启动一个线程,多线程效率高,startStopExecutor.submit(new StartChild(children[i]))
  • 启动管道和Valve阀链表,pipeline.start()
  • 设置生命周期为start
  • 启动Daemon线程ContainerBackgroundProcessor,该线程定期循环(while)调用 backgroundProcess()

ContainerBase.backgroundProcess()方法:

  • Cluster、 Realm、Pipeline的backgroundProcess()
  • fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null)

 StandardContext.backgroundProcess()方法:

  • Loader.backgroundProcess():Context.reload()
  • Manager.backgroundProcess():Session.processExpires()
  • WebResourceRoot.backgroundProcess():Cache.backgroundProcess()、StandardRoot.gc()
  • InstanceManager.backgroundProcess()

  • ContainerBase.backgroundProcess()
 StandardWrapper.backgroundProcess()方法:
  • ContainerBase.backgroundProcess():
  • JspServlet.periodicEvent():jsp生成的servlet定期进行检查


责任链设计模式:

每个容器都有一个pipeline模块和valve模块,在容器对象构造时自动生成。pipeline是每个容器的逻辑总线,pipeline按照配置的顺序加载各个valve。 valve存放的方式并非统一存放在pipeline中,而是一个链表一个接着一个。


Connector的Adapter将request和response传给Container的管道,从Engine的管道一路处理到Wrapper的管道,Wrapper再将response一路返回给Engine,然后Engine将response返回给Connector,Connector再返回给用户浏览器。

详见org.apache.catalina.connector.CoyoteAdapter的service()方法 。

四个container相当于四个生产线(Pipeline),四个Pipeline都这么干,直到最后的。

StandardWrapperValve拿到资源开始调用servlet。完成后返回来,一步一步的valve按照刚才丢生产原料是的顺序的倒序一次执行。如此才完成了tomcat的Pipeline的机制。所有的vavle执行完毕后,整个响应的也就结束了。

pipeline的实现类StandardPipeline:

public StandardPipeline(Container container) {

    super();
    setContainer(container);

}复制代码
@Override
public Container getContainer() {
    return this.container;
}复制代码
@Override
public void setContainer(Container container) {
    this.container = container;
}复制代码
@Override
public Valve[] getValves() {

    List<Valve> valveList = new ArrayList<>();
    Valve current = first;
    if (current == null) {
        current = basic;
    }
    while (current != null) {
        valveList.add(current);
        current = current.getNext();
    }

    return valveList.toArray(new Valve[0]);

}复制代码
@Override
public Valve getFirst() {
    if (first != null) {
        return first;
    }

    return basic;
}复制代码
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Start the Valves in our pipeline (including the basic), if any
    Valve current = first;
    if (current == null) {
        current = basic;
    }
    while (current != null) {
        if (current instanceof Lifecycle)
            ((Lifecycle) current).start();
        current = current.getNext();
    }

    setState(LifecycleState.STARTING);
}复制代码
@Override
protected synchronized void stopInternal() throws LifecycleException {

    setState(LifecycleState.STOPPING);

    // Stop the Valves in our pipeline (including the basic), if any
    Valve current = first;
    if (current == null) {
        current = basic;
    }
    while (current != null) {
        if (current instanceof Lifecycle)
            ((Lifecycle) current).stop();
        current = current.getNext();
    }
}复制代码
@Override
protected void destroyInternal() {
    Valve[] valves = getValves();
    for (Valve valve : valves) {
        removeValve(valve);
    }
}复制代码
@Override
public boolean isAsyncSupported() {
    Valve valve = (first!=null)?first:basic;
    boolean supported = true;
    while (supported && valve!=null) {
        supported = supported & valve.isAsyncSupported();
        valve = valve.getNext();
    }
    return supported;
}复制代码

Valve在处理完自己的事情以后,只需要将工作委托给下一个和自己在同一管道的阀门。

getNext().invoke(request, response)复制代码
@Override
public void addValve(Valve valve) {

    // Validate that we can add this Valve
    if (valve instanceof Contained)
        ((Contained) valve).setContainer(this.container);

    // Start the new component if necessary
    if (getState().isAvailable()) {
        if (valve instanceof Lifecycle) {
            try {
                ((Lifecycle) valve).start();
            } catch (LifecycleException e) {
                log.error("StandardPipeline.addValve: start: ", e);
            }
        }
    }

    // Add this Valve to the set associated with this Pipeline
    if (first == null) {
        first = valve;
        valve.setNext(basic);
    } else {
        Valve current = first;
        while (current != null) {
            if (current.getNext() == basic) {
                current.setNext(valve);
                valve.setNext(basic);
                break;
            }
            current = current.getNext();
        }
    }

    container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}复制代码
@Override
public void removeValve(Valve valve) {

    Valve current;
    if(first == valve) {
        first = first.getNext();
        current = null;
    } else {
        current = first;
    }
    while (current != null) {
        if (current.getNext() == valve) {
            current.setNext(valve.getNext());
            break;
        }
        current = current.getNext();
    }

    if (first == basic) first = null;

    if (valve instanceof Contained)
        ((Contained) valve).setContainer(null);

    if (valve instanceof Lifecycle) {
        // Stop this valve if necessary
        if (getState().isAvailable()) {
            try {
                ((Lifecycle) valve).stop();
            } catch (LifecycleException e) {
                log.error("StandardPipeline.removeValve: stop: ", e);
            }
        }
        try {
            ((Lifecycle) valve).destroy();
        } catch (LifecycleException e) {
            log.error("StandardPipeline.removeValve: destroy: ", e);
        }
    }

    container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
}复制代码

ValveBase是负责衔接各个管道(Pipeline)的,它负责将请求传递给下个管道的第一个阀门处理,它是每个管道中最后一个阀门,可以说基础阀是两个容器之间的桥梁。 基础阀的作用就是连接当前容器的下一个容器(通常是自己的子容器)。

@Override
public void setBasic(Valve valve) {

    // Change components if necessary
    Valve oldBasic = this.basic;
    if (oldBasic == valve)
        return;

    // Stop the old component if necessary
    if (oldBasic != null) {
        if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
            try {
                ((Lifecycle) oldBasic).stop();
            } catch (LifecycleException e) {
                log.error("StandardPipeline.setBasic: stop", e);
            }
        }
        if (oldBasic instanceof Contained) {
            try {
                ((Contained) oldBasic).setContainer(null);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
            }
        }
    }

    // Start the new component if necessary
    if (valve == null)
        return;
    if (valve instanceof Contained) {
        ((Contained) valve).setContainer(this.container);
    }
    if (getState().isAvailable() && valve instanceof Lifecycle) {
        try {
            ((Lifecycle) valve).start();
        } catch (LifecycleException e) {
            log.error("StandardPipeline.setBasic: start", e);
            return;
        }
    }

    // Update the pipeline
    Valve current = first;
    while (current != null) {
        if (current.getNext() == oldBasic) {
            current.setNext(valve);
            break;
        }
        current = current.getNext();
    }

    this.basic = valve;

}复制代码
@Override
public Valve getBasic() {
    return this.basic;
}复制代码

StandardEngineValve:获取对应的站点(Host)

host.getPipeline().getFirst().invoke(request, response)复制代码

StandardHostValve:

context.getPipeline().getFirst().invoke(request, response)复制代码

StandardContextValve

  • Request请求路径校验,不能WEB-INF和META-INF目录下的资源 
  • 根据Request选择合适的Wrapper 
  • Response.sendAcknowledgement(),TCP/IP协议中,如果接收方成功的接收到数据,会回复一个ACK数据
  • wrapper.getPipeline().getFirst().invoke(request, response)

StandardWrapperValve

每个Wrapper都封装着一个servlet,所以Wrapper和servlet有着很深的联系。StandardWrapper里会加载他代表的servlet并创建实例(即调用它的init方法),但不会调用servlet的service方法。StandardWrapperValve会调用sertvlet的service方法,其具体执行顺序如下 

  • 分配一个Servlet实例,因为StandardWrapper负责加载servlet,所以也是从Wrapper中获取servlet。 
  • 执行Servlet相关的所有过滤器:ApplicationFilterChain可以看做是一个“过滤器链”,StandardWrapperValve中的invoke会先创建该类的实例,然后调用它的doFilter方法,即从该链中的第一个过滤器开始调用。如果执行到了最后一个过滤器,就开始调用Servlet的service方法。 
  • 关闭过滤器链 
  • 通知Wrapper,重新委派处理完毕的servlet。


请求通过管道流转到Wrapper容器的管道,经过若干阀门后到达基础阀门StandardWrapperValve,它将创建一个过滤器链Application FilterChain对象,创建时过滤器链对象做如下逻辑处理:

  • 从Context容器中获取所有过滤器的相关信息
  • 通过URL匹配过滤器,匹配的加入到过滤器中
  • 通过Servlet名称匹配过滤器,匹配的加入到过滤器链中

创建ApplicationFilterChain对象后,StandardWrapperValve将调用它的doFilter方法,它就会开始一个一个调用过滤器,请求被一层层处理,最后才调Servlet处理。至此,针对某个请求,过滤器链将Context中所有过滤器中对应该请求的过滤器串联起来,实现过滤器功能。

根据请求资源的不同种类,可以把Servlet分成三种类别,比如请求可能访问一个普通的Servlet,也可能访问一个JSP页面,也可能访问一个静态资源。根据对这些不同类别的处理方式,可以分为三种Servlet。如上图所示,一个请求到达Tomcat后将由URI映射器根据URI进行建模,它会计算出该请求该发往哪个Host容器的哪个Context容器的哪个Wrapper处理,在路由到Wrapper容器时会通过一定的算法选择不同的Servlet进行处理。比如,普通Servlet请求则路由到普通Servlet,JSP页面则路由到JspServlet,而静态资源则路由到DefaultServlet。

Tomcat的客户端请求由管道处理,最后会通过Wrapper容器的管道,这时它会调用Servlet实例的service方法进行逻辑处理,处理完后响应客户端。整个处理由Tomcat的Executor线程池的线程处理,而线程池的最大线程数是有限制的,所以这个处理过程越短,就能越快地将线程释放回线程池。但如果Servlet中的处理逻辑耗时越长,就会导致长期地占用Tomcat的处理线程池,影响Tomcat的整体处理能力


Service的Mapper组件:Tomcat在处理请求时对请求的路由分发全由Mapper组件负责,提供请求路径的路由映射,根据某个请求路径通过计算得到相应的Servlet(Wrapper)。如果要将整个tomcat容器中所有的web项目以能够以Servlet级别组织起来,需要一个多层级的类似Map结构的存储空间。如下图,以Mapper作为映射的入口,按照容器等级首先会包含了N个Host容器的引用,然后每个Host会有N个Context容器的引用,最后每个Context容器包含N个Wrapper容器的引用。

MappedHost—>MappedContext—>MappedWrapper


<Host name="www.baidu.com" appBase="webapps" autoDeploy="true">

<Context path="/apoms" docBase=" /usr/local/apoms"/>

</Host>

每个<Context>元素表示一个应用,如果应用在<Host>的appBase指定的目录下,可以不配置<Context>元素,如果是外部应用,那么就必须配置<Context>,如果指定path为空path="", 则默认访问的项目就是/apoms, 而不再是webapps下的ROOT。

CATALINA-HOME/webapps/中的应用启动是放到线程池中进行异步运行的,

在org.apache.catalina.startup.HostConfig#deployApps()方法中。

应用的启动在StandardHost#start的时候,通过Lifecycle.START_EVENT这个事件的监听器HostConfig进行进一步的启动。

类图:www.processon.com/view/link/5…

启动停止:www.processon.com/view/link/5…