天天看點

Tomcat優化

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優化

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配置沒什麼可優化的。

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記憶體大小等。