Tomcat优化

  1. 云栖社区>
  2. 博客>
  3. 正文

Tomcat优化

~信~仰~ 2019-03-16 18:16:29 浏览1518
展开阅读全文

tomcat架构

tomcat目录

(1)bin:主要用来存放命令,.bat是windows下,.sh是Linux下

(2)conf:主要用来存放tomcat的一些配置文件

(3)lib:存放tomcat依赖的一些jar包

(4)logs:存放tomcat在运行时产生的日志文件

(5)temp:存放运行时产生的临时文件

(6)webapps:存放应用程序

(7)work:存放tomcat运行时编译后的文件,比如JSP编译后的文件

tomcat架构

tomcat_

Server元素在最顶层,代表整个Tomcat容器,因此它是server.xml中唯一一个最外层的元素。Server的作用是提供接口让客户端能够访问到这个Service集合,同时维护它所包含的所有的Service的声明周期,包括如何初始化、如何结束服务、如何找到客户端要访问的Service。

一个Server元素中可以包含多个Service。

Service的作用是组装Connector和Engine对外提供服务。一个Service可以包含多个Connector,但是只能包含一个Engine。

Connector的作用是接收连接请求,创建Request和Response对象用于和请求端交换数据;然后分配线程携带该request和response交给绑定的Engine来处理,最后把处理后的Request和Response返回给客户端。

executor是线程池,tomcat提供了默认线程池,线程池配置可以在源码中查找Executor:

public interface Executor extends java.util.concurrent.Executor, Lifecycle {...}

然后找到其实现类:

public class StandardThreadExecutor extends LifecycleMBeanBase implements Executor, ResizableExecutor {
    ...
    protected String namePrefix = "tomcat-exec-";
    protected int maxThreads = 200;
    protected int minSpareThreads = 25;
    protected int maxIdleTime = 60000;
    protected int maxQueueSize = Integer.MAX_VALUE;
    ...
}

在实际中我们可以根据实际项目情况添加自己的线程池。

Engine是Service组件中的请求处理组件。engine从Connector中接收请求并处理,并将处理后的response返回Connector。
engine可以包含多个host。

一个Host代表Engine中的一个虚拟主机。Host的作用是运行多个Web应用(一个Context代表一个Web应用),并负责安装、展开、启动和结束每个Web应用。Host组件至少有一个,且其中一个的name必须与Engine组件的defaultHost属性相匹配。
客户端通过主机名来访问服务器。Tomcat从HTTP头中提取出主机名,寻找名称匹配的host。如果没有匹配,请求将发送至默认主机。

一个host可以包含多个context。

context元素代表在指定host上运行的一个web应用。这个web应用对应一个war文件,或者war文件解压后对应的目录。

刚下载的tomcat,server.xml文件中没有context元素,因为默认tomcat的host开启了自动部署。

tomcat优化

tomcat优化可以有几个方向,配置项的删除,配置参数的修改,jdk参数修改。

配置项的删除

tomcat涉及的配置文件有三种,web项目中自带的WEB-INF/web.xml,tomcat自带的conf/web.xml以及conf/server.xml文件。

tomcat在启动时,会将web项目中WEB-INF/web.xml和自己的conf/web.xml进行合并,然后在ContextConfig类下的configureContext()统一解析这些内容:

private void configureContext(WebXml webxml) {
    ...
    for (Entry<String, String> entry : webxml.getContextParams().entrySet()) {
        context.addParameter(entry.getKey(), entry.getValue());
    }
    for (ContextEjb ejbRef : webxml.getEjbRefs().values()) {
        context.getNamingResources().addEjb(ejbRef);
    }
    for (ContextEnvironment environment : webxml.getEnvEntries().values()) {
        context.getNamingResources().addEnvironment(environment);
    }
    for (ErrorPage errorPage : webxml.getErrorPages().values()) {
        context.addErrorPage(errorPage); // 解析错误页面
    }
    for (FilterDef filter : webxml.getFilters().values()) { // 解析过滤器标签
        if (filter.getAsyncSupported() == null) {
            filter.setAsyncSupported("false");
        }
        context.addFilterDef(filter);
    }
    ...
}

因为这些xml文件都会被tomcat解析,所以优化方向是将这三类文件中不必要的配置删除。

WEB-INF/web.xml

这个只能是在项目中不需要的配置不要放进去,有过期的配置要及时删除,减少tomcat的解析成本。

conf/web.xml
DefaultServlet
  <!-- The default servlet for all web applications, that serves static     -->
  <!-- resources.  It processes all requests that are not mapped to other   -->
  <!-- servlets with servlet mappings (defined either here or in your own   -->
  <!-- web.xml file).   -->
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

这是一个处理静态文件的servlet,如果项目中不需要tomcat进行静态文件的处理,可以将其删掉。

JspServlet
  <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
  <!-- used by Tomcat to support JSP pages.  Traditionally, this servlet    -->
  <!-- is mapped to the URL pattern "*.jsp".      -->
    <servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>

这个servlet是用来编译和执行jsp文件的,如果项目中没有jsp文件,可以将其删掉。

jsp映射

项目中没有jsp,关于jsp下面的映射也可以删除:

    <!-- The mappings for the JSP servlet -->
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
mime类型映射
  <!-- ===================== Default MIME Type Mappings =================== -->
  <!-- When serving static resources, Tomcat will automatically generate    -->
  <!-- a "Content-Type" header based on the resource's filename extension,  -->
  <!-- based on these mappings.  Additional mappings can be added here (to  -->
  <!-- apply to all web applications), or in your own application's web.xml -->
  <!-- deployment descriptor.                                               -->
  <!-- Note: Extensions are always matched in a case-insensitive manner.    -->
    ...
    <mime-mapping>
        <extension>jpg</extension>
        <mime-type>image/jpeg</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>jpgm</extension>
        <mime-type>video/jpm</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>jpgv</extension>
        <mime-type>video/jpeg</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>jpm</extension>
        <mime-type>video/jpm</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>jsf</extension>
        <mime-type>text/plain</mime-type>
    </mime-mapping>
    <mime-mapping>
        <extension>json</extension>
        <mime-type>application/json</mime-type>
    </mime-mapping>
    ...

文件中提供的mime类型非常多,如果有确定项目中不会涉及到的类型,可以删除其映射配置。

欢迎页配置
  <!-- ==================== Default Welcome File List ===================== -->
  <!-- When a request URI refers to a directory, the default servlet looks  -->
  <!-- for a "welcome file" within that directory and, if present, to the   -->
  <!-- corresponding resource URI for display.                              -->
  <!-- If no welcome files are present, the default servlet either serves a -->
  <!-- directory listing (see default servlet configuration on how to       -->
  <!-- customize) or returns a 404 status, depending on the value of the    -->
  <!-- listings setting.                                                    -->
  <!--                                                                      -->
  <!-- If you define welcome files in your own application's web.xml        -->
  <!-- deployment descriptor, that list *replaces* the list configured      -->
  <!-- here, so be sure to include any of the default values that you wish  -->
  <!-- to use within your application.                                       -->

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

如果不需要展示tomcat的欢迎页,可以将其删掉。

server.xml
Listener

下面是tomcat8.5版本提供的默认listener列表:

<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 className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<!-- 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" />
  • VersionLoggerListener

作用是在Tomcat初始化时,打印一下Tomcat相关的版本信息以及操作系统和jdk环境信息,可以删掉。

  • AprLifecycleListener

Tomcat启动时,检查APR库,如果存在则加载,这个配置仅当connector的protocol设置为AJP/1.3时有用,如果非AJP/1.3,可以删除。

  • JreMemoryLeakPreventionListener

Java运行环境可能导致某些已知位置的内存泄漏或文件锁定,JreMemoryLeakPreventionListener提供这些情况的解决方案。

  • GlobalResourcesLifecycleListener

作用于全局资源,通过该监听器,初始化标签中定义的全局JNDI资源;如果没有该监听器,定义的全局资源都不能使用。如果不使用GlobalNamingResources定义全局资源,可以删除。

  • ThreadLocalLeakPreventionListener

Web应用因thread-local导致的内存泄露而要停止时,该监听器会触发线程池中线程的更新。只有当Web应用(即Context元素)的renewThreadsWhenStoppingContext属性设置为true时,该监听器才有效。

官方文档对renewThreadsWhenStoppingContext配置的解释为:

If true, when this context is stopped, Tomcat renews all the threads from the thread pool that was used to serve this context. 
This also requires that the ThreadLocalLeakPreventionListener be configured in server.xml and that the threadRenewalDelay property of the Executor be >=0. 
If not specified, the default value of true will be used.
GlobalNamingResources
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

GlobalNamingResources可以定义全局资源,可以看出,这个tomcat的默认配置是通过读取$TOMCAT_HOME/ conf/tomcat-users.xml实现的。tomcat-user.xml用于定义tomcat管理页面相关配置,如果不登录管理界面可以删掉。

Connector

tomcat默认配置包含如下connector:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

官网中对于这个connector有如下描述:

The AJP Connector element represents a Connector component that communicates with a web connector via the AJP protocol. This is used for cases where you wish to invisibly integrate Tomcat into an existing (or new) Apache installation, and you want Apache to handle the static content contained in the web application, and/or utilize Apache's SSL processing.

可知当tomcat需要集成到Apache服务器时才使用这个connector,现在一般都用Nginx代替Apache,所以不使用Apache的话这个也可以删掉。

Realm
<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>

Realm,可以把它理解成“域”;Realm提供了一种用户密码与web应用的映射关系,从而达到角色安全管理的作用,tomcat默认的这个realm是和name为UserDatabase的资源绑定的,而该资源在Server元素中使用GlobalNamingResources配置。如果不需要可以删掉。

Valve
<!-- 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"
       prefix="localhost_access_log" suffix=".txt"
       pattern="%h %l %u %t &quot;%r&quot; %s %b" />

Valve的意思是“阀门”,不同的Valve有不同的特性,AccessLogValve的作用是记录其所在容器处理的所有请求,默认配置中的Valve放在Host下,便可以记录该Host处理的所有请求。AccessLogValve记录的日志就是访问日志,每天的请求会写到一个日志文件里。现在一般记录Nginx访问日志,这个也可以删除。

配置项参数优化

参数优化主要是优化sever.xml中的配置参数,示例server.xml如下:

<?xml version='1.0' encoding='utf-8'?>

<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <Service name="Catalina">
    <Connector port="8002" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8444" />
    <Engine name="Catalina" defaultHost="wggl">
      <Host name="wggl" appBase="wggl" unpackWARs="true" autoDeploy="false">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="wggl_accesslog" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        <Context docBase="ROOT" path="/" reloadable="false"  sessionCookieName="wggl"/>
      </Host>
    </Engine>
  </Service>
  <Service name="Catalina">
    <Connector port="8003" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8444" />
    <Engine name="Catalina" defaultHost="jxkh">
      <Host name="jxkh" appBase="jxkh" unpackWARs="true" autoDeploy="false">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="jxkh_accesslog" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        <Context docBase="ROOT" path="/" reloadable="false"  sessionCookieName="jxkh"/>
      </Host>
    </Engine>
  </Service>
</Server>
server

port:指定一个端口,这个端口负责监听关闭tomcat的请求。

shutdown:指定向端口发送的命令字符串。

sever配置没什么可优化的。

service

name:指定service的名字。

service配置没什么可优化的。

Connector

port:指定服务器端要创建的端口号,并在这个断口监听来自客户端的请求

minProcessors:服务器启动时创建的处理请求的线程数

maxProcessors:最大可以创建的处理请求的线程数

enableLookups:如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址

redirectPort:指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号

acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理

maxConnections:达到这个值之后,将继续接受连接,但是不处理,能继续接受多少根据acceptCount的值

minSpareThreads:最小空闲线程数

connectionTimeout:指定超时的时间数(以毫秒为单位)

官网8.5版本connector使用连接类型如下:

org.apache.coyote.ajp.AjpNioProtocol - non blocking Java NIO connector.
org.apache.coyote.ajp.AjpNio2Protocol - non blocking Java NIO2 connector.
org.apache.coyote.ajp.AjpAprProtocol - the APR/native connector.

查看Connector源码:

public void setProtocol(String protocol) {
    ...
    if ("HTTP/1.1".equals(protocol) || protocol == null) {
        if (aprConnector) {
            setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
        } else {
            setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
        }
    } else if ("AJP/1.3".equals(protocol)) {
        if (aprConnector) {
            setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
        } else {
            setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
        }
    } else {
        setProtocolHandlerClassName(protocol);
    }
}

可知默认协议使用的是Http11NioProtocol,即NIO方式。

tomcat默认连接池有限制,可以为connector配置自己的连接池,例如:

<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="1000" minSpareThreads="4"/>

在连接池中可以根据项目自定义最大线程数量。

Engine

name:属性用于日志和错误信息,在整个Server中应该唯一

defaultHost:defaultHost属性指定了默认的host名称,当发往本机的请求指定的host名称不存在时,一律使用defaultHost指定的host进行处理;因此defaultHost的值,必须与Engine中的一个Host组件的name属性值匹配

没什么可优化的。

host

name:指定虚拟主机的主机名,一个Engine中有且仅有一个Host组件的name属性与Engine组件的defaultHost属性相匹配;一般情况下,主机名需要是在DNS服务器中注册的网络名,但是Engine指定的defaultHost不需要

appBase:应用程序基本目录,即存放应用程序的目录

unpackWARs:指定了是否将war文件解压,如果为true,则通过解压后的目录运行该Web应用;如果为false,则直接使用WAR文件运行Web应用

autoDeploy:是否自动部署,Tomcat运行期间会用一个线程定时检查,如果有新的web工程会自动部署。

autoDeploy生产环境之下可以改成false,减少tomcat的负担。

autoDeploy有需要注意的点,如果为true,拷贝war到webapp下面,tomcat自动解压并部署。但是停掉tomcat拷贝新的war包过去,tomcat不会解压新包并后覆盖旧目录,因为tomcat直接使用了之前解压过的目录。官网有描述:

If you redeploy an updated WAR file, be sure to delete the expanded directory when restarting Tomcat, so that the updated WAR file will be re-expanded (note that the auto deployer, if enabled, will automatically expand the updated WAR file once the previously expanded directory is removed).

Context

docBase:代表应用程序或war文件存放的路径,这个可以自由指定,例如d:/study

path:表示此web应用程序的url的前缀,这样请求的url为http://localhost:8080/path/**

reloadable:如果为true,则tomcat会自动检测应用程序的/WEB-INF/lib和/WEB-INF/classes目录的变化,自动装载新的应用程序,可以在不重起tomcat的情况下改变应用程序

生产环境中可以把reloadable设置为false。

Realm(表示存放用户名,密码及role的数据库)

className:指定Realm使用的类名,此类必须实现org.apache.catalina.Realm接口

没什么可优化的。

Valve(功能与Logger差不多,其prefix和suffix属性解释和Logger 中的一样)

className:规定了Valve的类型;例如tomcat默认的是AccessLogValve。

directory:指定日志存储的位置,默认日志存储在$TOMCAT_HOME/logs目录下。

prefix:指定了日志文件的前缀。

suffix:指定了日志文件的后缀。通过directory、prefix和suffix的配置,在$TOMCAT_HOME/logs目录下,可以看到如下所示的日志文件。

没什么可优化的。

jvm参数优化

tomcat是用Java写的,就要运行在jvm上,垃圾处理方式等都要遵循jvm的方式。tomcat中设置jvm参数在 catalina.sh(Linux)和catalina.bat(windows)中,以JAVA_OPTS变量存储。以catalina.sh为例:

JAVA_OPTS="-Dfile.encoding=UTF8"
JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS"

可以在该变量中添加jvm参数,达到减少gc次数等目标,例如根据tomcat所在服务器修改jvm内存大小等。

网友评论

登录后评论
0/500
评论
~信~仰~
+ 关注