How Tomcat works — 七、tomcat发布webapp

目录

  • 什么叫发布
  • webapp发布方式
  • reload
  • 总结

什么叫发布

发布就是让tomcat知道我们的程序在哪里,并根据我们的配置创建Context,进行初始化、启动,如下:

  • 程序所在的位置
  • 创建Context,添加到Host
  • 初始化(创建解析webxml的digester)
  • 启动(初始化filter、listener、servlet)

webapp发布方式

在tomcat 中发布webapp的方式不同会导致app启动的先后顺序不一样(这里按照启动顺序或者时机不同进行划分):

  1. 在xml中配置,在conf/server.xml中配置(在host标签内部)
  2. 直接将webapp文件夹、war包放在tomcat下面的"webapps"目录,或者在webapps下面新建一个xml(根标签为context,在属性中表明应用程序的位置)

其实两种发布方式只是在启动顺序上稍有不同,启动过程完全一致,先以在server.xml中配置说明

在server.xml进行配置

这情况在我们使用eclipse等工具进行开发发布的时候,eclipse会帮我们在server.xml的Host标签内添加Context,如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
--><!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 --><Server port="8006" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
  <Listener className="org.apache.catalina.core.JasperListener"/>
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
  </GlobalNamingResources>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->

    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL HTTP/1.1 Connector on port 8080
    -->
    <Connector connectionTimeout="20000" port="8888" protocol="HTTP/1.1" redirectPort="8445"/>
    <!-- A "Connector" using the shared thread pool-->
    <!--
    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    -->
    <!-- Define a SSL HTTP/1.1 Connector on port 8443
         This connector uses the BIO implementation that requires the JSSE
         style configuration. When using the APR/native implementation, the
         OpenSSL style configuration is required as described in the APR/native
         documentation -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />
    -->

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8019" protocol="AJP/1.3" redirectPort="8443"/>

    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine defaultHost="localhost" name="Catalina">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>

            <Context docBase="/var/tomcat-test/webapps/TestTomcat" path="/TestTomcat" reloadable="true" source="org.eclipse.jst.jee.server:TestTomcat"/>
        </Host>
    </Engine>
  </Service>
</Server>

在新建server的时候会解析server.xml,同时也会根据我们上面的配置新建StandardContext,而且会将StandardContext作为Host的一个Child(可以在Catalina.createStartDigester方法中查看怎么解析server.xml),在tomcat的启动过程中context是由父容器host启动的

额,看到这张图发现似曾相识,对滴,就是Context的初始化,因为在tomcat中一个context实例就代表一个webapp,所以其实应用程序webapp的发布本身就是context的初始化和启动,再看看context的启动

以上已经完整展示了一个webapp的发布过程,也就是在server.xml的配置的webapp发布过程,接着来看看在webapps目录下的发布过程。

在webapps目录下

在StandardHost的startInternal方法中调用了父类的ContainerBase.startInternal方法,在StandardHost发布完在server.xml中配置的app之后,会调用setState来切换自身的状态,这个时候就会触发listener HostConfig的lifecycleEvent方法

@Override
protected synchronized void startInternal() throws LifecycleException {

    // Start our subordinate components, if any
    if ((loader != null) && (loader instanceof Lifecycle))
        ((Lifecycle) loader).start();
    logger = null;
    getLogger();
    if ((manager != null) && (manager instanceof Lifecycle))
        ((Lifecycle) manager).start();
    if ((cluster != null) && (cluster instanceof Lifecycle))
        ((Lifecycle) cluster).start();
    Realm realm = getRealmInternal();
    if ((realm != null) && (realm instanceof Lifecycle))
        ((Lifecycle) realm).start();
    if ((resources != null) && (resources instanceof Lifecycle))
        ((Lifecycle) resources).start();

    // Start our child containers, if any   // 因为在解析server.xml的时候已经新建了Context,这里就可以直接start
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<Future<Void>>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }

    boolean fail = false;
    for (Future<Void> result : results) {
        try {
            result.get();
        } catch (Exception e) {
            log.error(sm.getString("containerBase.threadedStartFailed"), e);
            fail = true;
        }

    }
    if (fail) {
        throw new LifecycleException(
                sm.getString("containerBase.threadedStartFailed"));
    }

    // Start the Valves in our pipeline (including the basic), if any
    if (pipeline instanceof Lifecycle)
        ((Lifecycle) pipeline).start();

   // 调用lifecycleEvent方法,触发StandardHost的start事件,触发监听器HostConfig的start方法,所有在webapps下面的都在start方法中发布
    setState(LifecycleState.STARTING);

    // Start our thread
    threadStart();

}

调用过程如下:

(上图中主要画出了deployWars的调用过程,其他两个deploy方法也类似)

在HostConfig.deployApps方法中主要进行了:

  • 调用deployDescriptors:发布所有使用xml配置的webapp,因为可能有多个xml,所以在该方法内部又调用了deployDescriptor来发布每个xml对应的webapp
  • 调用deployWars:发布所有在webapps目录下的war包,也可能存在多个
  • 调用deployDirectories:发布所有直接部署在webapps目录下的应用程序

前面说过了,发布webapp就是新建一个context对象并初始化、启动,上面三个方法中主要的作用为:

Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
    (LifecycleListener) clazz.newInstance();
context.addLifecycleListener(listener);

context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName() + ".war");
host.addChild(context);
  • 构建一个StandardContext
  • 将context添加到host中,在Container.addChildInternal方法中会调用context.start

接下来的步骤就和上面在server.xml配置的webapp启动一致了。

reload

在我们使用tomcat开发web的时候经常会用到“热加载”(热部署)功能,那么原理究竟是什么呢?上面介绍了webapp发布,因为reload功能也是从HostConfig开始的,所以继续介绍reload功能

上面的ContainerBase.startInternal是由StandardEngine.startInternal方法调用的,启动了一个daemon线程定时检测webapp是否发生变化(文件是否被修改),如果被修改了则会重新启动StandardContext。

总结

像tomcat部署和发布,我们天天都在用,可是不知道究竟原理怎么样的,在学习了tomcat源码之后,对这一切都更加明了。知其然,知其所以然。

时间: 2024-10-14 20:09:53

How Tomcat works — 七、tomcat发布webapp的相关文章

how tomcat works 七 日志记录器

大家可以松一口气了,这个组件比较简单,这一节和前面几节想比,也简单的多. Logger接口 Tomcat中的日志记录器都必须实现org.apache.catalina.Logger接口. package org.apache.catalina; import java.beans.PropertyChangeListener; public interface Logger { public static final int FATAL = Integer.MIN_VALUE; public s

How Tomcat works — 四、tomcat启动(3)

上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看container. 目录 Pipeline和Vavle StandardEngine类和StandardHost类 StandardContext类 总结 Pipeline和Vavle 在第二节(How Tomcat works — 二.tomcat启动(1))中没有介绍关于Pipeline和Vavle,因

How Tomcat Works 2

上一节(How Tomcat Works 1 编写一个简单静态web服务器)编写了一个简单的web服务器,只能处理静态的资源,本节将继续向前迈出一个小步,创建两个不同的servlet容器,能够利用servlet简单的处理动态内容.注意每节的代码都是基于上一节的继续丰富,因此有必要从第一节开始看起. 在编写代码之前,需要先大体了解一下Servlet是什么,方便后面的理解,下面就是一个最简单的Servlet什么也没做: package prymont; import java.io.IOExcept

how tomcat works 读书笔记四 tomcat的默认连接器

其实在第三章,就已经有了连接器的样子了,不过那只是一个学习工具,在这一章我们会开始分析tomcat4里面的默认连接器. 连接器 Tomcat连接器必须满足以下几个要求 1 实现org.apache.cataline.Connector接口 2 负责创建实现了org.apache.cataline.Request接口的request对象 3 负责创建实现了org.apache.cataline.Response接口的response对象 这里默认的连接器的原理很简单,就是等待http请求,创建re

《How Tomcat Works》读书笔记(二)

<How Tomcat Works>读书笔记(二) 这是<How Tomcat Works>第一二章的读书笔记.第一张主要写了一个静态资源处理的web服务器,第二章加了对servlet的处理. 1. 概述 1.1 架构 HttpServer:表示Http服务器,与客户端通信,处理Http请求. StaticResourceProcessor:对静态资源请求进行处理. ServletProcessor:对Servlet资源请求进行处理. Request:表示Http请求,实现了Ser

how tomcat works 读书笔记(二)----------一个简单的servlet容器

app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/article/details/39378157) 回顾我们上一章,我们开发了一个最最简单的web服务器,它可以使用户访问服务器内的静态资源.当然这是远远不够的,在这一节里,我们就试着让服务器在能相应静态资源的基础上继续支持servlet. servlet接口 javax.servlet.Servlet接口

tomcat通过conf-Catalina-localhost目录发布项目详解 摘自:http://www.cnblogs.com/iyangyuan/archive/2013/09/12/3316444.html

Tomcat发布项目的方式大致有三种,但小菜认为通过在tomcat的conf/Catalina/localhost目录下添加配置文件,来发布项目,是最佳选择. 因为这样对tomcat的入侵性最小,只需要新增一个配置文件,不需要修改原有配置:而且支持动态解析,修改完代码直接生效(修改配置除外). 但是网上关于这种方法的介绍很简单,小菜来补充一下. 1.直接在eclipse中添加一个server,添加过程中指明tomcat的路径即可. 2.在tomcat服务器的conf\Catalina\local

how tomcat works

目标 深入剖析tomcat •Java Web 服务性能优化实践2014-11-04阅读225 •How Tomcat work之第十五章之Digester之setting property2014-07-30阅读253 •How Tomcat work 之第十四章之server and services 之application2014-07-29阅读194 •how tomcat work之第十四章:server and service2014-07-27阅读352 •How Tomcat

Tomcat 没有自动解压webapp下的war项目文件问题

默认选择的tomcat安装在了C盘下的C:\Program Files下 所以webapp文件也在C盘下 选择启动tomcat时 我选择了 bin下的 Tomcat.exe 显示成功启动 打开项目网站显示404   到webapp文件下发现未解压war文件 选择bin下的startup.bat打开tomcat 显示报错 [java] view plain copy java.io.IOException: Unable to create the directory [C:\Program Fi