你的位置:首页 > Java教程

[Java教程]How Tomcat works — 七、tomcat发布webapp


目录

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

什么叫发布

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

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

webapp发布方式

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

  1. 直接将webapp文件夹、war包放在tomcat下面的"webapps"目录,或者在webapps下面新建一个

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

在server.

这情况在我们使用eclipse等工具进行开发发布的时候,eclipse会帮我们在server.

<? 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. 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>

View Code

在新建server的时候会解析server.

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

以上已经完整展示了一个webapp的发布过程,也就是在server.

在webapps目录下

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

@Overrideprotected 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. 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:发布所有使用
  • 调用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.

reload

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

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

总结

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