Tomcat原理剖析及性能调优
1? ?Tomcat的原理和配置文件詳解
1.1 tomcat服務器總體架構
1.2 Tomcat連接器組件Coyote
1.3?Coyote內部組件功能
1.4 Tomcat Catalina組件
1.5?Tomcat配置文件詳解
2? Tomcat如何處理一個請求(源碼追蹤)
3? Tomcat性能調優
3.1?JVM內存模型及JVM內存參數調優
3.2 tomcat配置調優
1? ?Tomcat的原理和配置文件詳解
1.1 tomcat服務器總體架構
????????tomcat服務器具備兩種能力,一種是作為一個Http服務器,另外一個則是作為Servlet容器,兩大功能將tomcat拆分為兩個組件,Coyote(連接器)與Catalina(容器)兩個組件。其中Coyote負責處理Socket通訊,將Socket解析封裝為Request對象(非Java ee的Request對象),Catalina負責Servlet初始化、加載等。
1.2 Tomcat連接器組件Coyote
????????在進行Coyote組件的分析之前,我們先來看下Coyote組件的職能:
? ? ? ? 1)處理socket請求
? ? ? ? 2)與Catalina組件解耦
????????3)將請求封裝成request對象,并將該對象轉發給Catalina組件封裝成HttpServletRequst對象
????????4)負責網絡層和傳輸層的內容
????????由上面我們可以知道,Tomcat連接器組件主要是處理TCP/IP模型下的網絡層和傳輸層的內容,具體的TCP/IP模型可參考如下圖。
????????Tomcat主要是職責是處理TCP/IP模型中的傳輸層和應用層,其中應用層支持的協議有HTTP(1.1與2.0版本,默認為1.1版本),傳輸層支持NIO和ARP兩種傳輸模型,這里具體的I/O傳輸模型這里不多做分析。
1.3?Coyote內部組件功能
| Coyote組件 | 功能 |
| Adapter | 位于org.apache.coyote包中,Tomcat中的Catalina組件入口,將tomcat的request和response 轉換成ServletRequest和HttpResponse,并用Catalina容器 |
| Processor | 所有協議處理器的通用接口(應用層協議),加工處理Socket連接并封裝成tomcat Request和Response對象,并通過Adapter傳入容器中 |
| ProtocolHandler | Tomcat協議接口,定義具體協議的處理能力。 |
| EndPoint | Coyote通訊端點,監聽Socket請求,向Processor提供字節流 |
發起請求------》EndPoint------提供字節流----->Processor---提供res和rep對象----》Adapter --提供ServletRequest、ServletResponse--》Catalina容器。
1.4 Tomcat Catalina組件
????????Tomcat的核心組件,可以認為所有的組件都是為Catalina組件服務,Catalina是一個Servlet容器。Tomcat啟動都會示例化一個Catalina實例,Catalina實例會讀取conf/server.xml這個配置文件完成其他組件的實例創建。查看Tomcat的conf/server.xml文件,Catalina 可以實例化一個Server容器和多個Service容器。Service下面可以配置多個Connector組件綁定到一個Container容器。
我們讀配置文件,可看到Catalina容器組件可由具體的以下幾種容器組件構成。
Engine: Catalina的引擎,在Service下只能有一個Engine
Host:一個站點,位于Engine下面,可以配置多個虛擬站點地址,可以包含多個Content
Content:一個站點程序,位于Host下面,可以包含多個Wrapper
Wrapper:一個Wrapper代表一個Servlet實例,容器最底層,不在包含其他容器
1.5?Tomcat配置文件詳解
Tomcat核心配置文件位于conf/server.xml。
<?xml version="1.0" encoding="UTF-8"?> <!-- 根節點port:為關閉服務的監聽端口shutdown:關閉服務的指令--> <Server port="8005" 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加載以及管理 --><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" /><!-- Global JNDI resourcesDocumentation at /docs/jndi-resources-howto.html全局命名服務--><GlobalNamingResources><!-- Editable user database that can also be used byUserDatabaseRealm 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><!-- A "Service" is a collection of one or more "Connectors" that sharea 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--><!--service共享線程池name: 線程池名稱,在Connector可指定使用的共享線程池名稱namePrefix:創建的線程池名稱前綴,線程名稱為namePrefix+threadNumbermaxThreads:最大線程池數量minSpareThreads:活躍線程數量,線程不會被銷毀maxIdlTime:線程空閑時間,超過該時間銷毀空線程,默認6000maxQueueSize: 線程池最大排隊數量,默認值為int最大值。超過這個值tomcat不在處理請求prestartminSpareThreads:啟動線程池時是否啟動 minSpareThreads部分線程。默認值為false,即不啟動threadPriority:線程池中線程優先級,默認值為5,值從1到10className:線程池實現類,未指定情況下,默認實現類為org.apache.catalina.core.StandardThreadExecutor。如果想使??定義線程池?先需要實現org.apache.catalina.Executor接?<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"maxThreads="150" minSpareThreads="4"/>--><Executor name="commonThreadPool"namePrefix="thread-exec-"maxThreads="200"minSpareThreads="100"maxIdleTime="60000"maxQueueSize="Integer.MAX_VALUE"prestartminSpareThreads="false"threadPriority="5"className="org.apache.catalina.core.StandardThreadExecutor"/><!--port:端?號,可設置為0,Tomcat隨機拿一個可以使用的端口給Connector使用。protocol: 訪問協議。 默認為 HTTP/1.1 , 并采??動切換機制選擇?個基于 JAVANIO 的鏈接器或者基于本地APR的鏈接器(根據本地是否含有Tomcat的本地庫判定)connectionTimeOut: 請求超時時間, 單位為 毫秒。 -1 表示不超時。redirectPort:HTTPS默認端口executor:制定使用線程池Engine 標簽Engine 表示 Servlet 引擎Host 標簽Host 標簽?于配置?個虛擬主機URIEncoding:?于指定編碼URI的字符編碼, Tomcat8.x版本默認的編碼為 UTF-8 , Tomcat7.x版本默認為ISO-8859-1--><!--org.apache.coyote.http11.Http11NioProtocol , ?阻塞式 Java NIO 鏈接器--><Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" /><!-- You should set jvmRoute to support load-balancing via AJP ie :<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">執行引擎名稱--><Engine name="Catalina" defaultHost="localhost"><!--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 passwordsvia a brute-force attack --><Realm className="org.apache.catalina.realm.LockOutRealm"><!-- This Realm uses the UserDatabase configured in the global JNDIresources under the key "UserDatabase". Any editsthat are performed against this UserDatabase are immediatelyavailable for use by the Realm. --><Realm className="org.apache.catalina.realm.UserDatabaseRealm"resourceName="UserDatabase"/></Realm><!--配置虛擬站點--><Host name="localhost" appBase="webapps"unpackWARs="true" autoDeploy="true"><Context docBase="/webapps/demo" path="/demo"></Context><!-- SingleSignOn valve, share authentication between web applicationsDocumentation at: /docs/config/valve.html --><!--<Valve className="org.apache.catalina.authenticator.SingleSignOn" />--><!-- Access log processes all example.Documentation at: /docs/config/valve.htmlNote: 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 "%r" %s %b" /></Host></Engine></Service> </Server>
2? Tomcat如何處理一個請求(源碼追蹤)
在進行源碼追蹤先,我們先下載tomcat-8.5.72-src源碼和tomcat-8.5.72,下載地址:Apache Tomcat? - Apache Tomcat 8 Software Downloads
?使用IDEA打開tomcat源碼工程
?因為tomcat 不是用maven構建的,將tomcat源碼工程新建pom.xml文件轉為maven項目,pom.xml的內容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.apache.tomcat</groupId><artifactId>apache-tomcat-8.5.50-src</artifactId><name>Tomcat8.5</name><version>8.5</version><build><!--指定源?錄--><finalName>Tomcat8.5</finalName><sourceDirectory>java</sourceDirectory><resources><resource><directory>java</directory></resource></resources><plugins><!--引?編譯插件--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><encoding>UTF-8</encoding><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build><!--tomcat 依賴的基礎包--><dependencies><dependency><groupId>org.easymock</groupId><artifactId>easymock</artifactId><version>3.4</version></dependency><dependency><groupId>ant</groupId><artifactId>ant</artifactId><version>1.7.0</version></dependency><dependency><groupId>wsdl4j</groupId><artifactId>wsdl4j</artifactId><version>1.6.2</version></dependency><dependency><groupId>javax.xml</groupId><artifactId>jaxrpc</artifactId><version>1.1</version></dependency><dependency><groupId>org.eclipse.jdt.core.compiler</groupId><artifactId>ecj</artifactId><version>4.5.1</version></dependency><dependency><groupId>javax.xml.soap</groupId><artifactId>javax.xml.soap-api</artifactId><version>1.4.0</version></dependency></dependencies> </project>如果你下載的是tomcat10+版本,還需要在pom中添加下面這兩個jar包:
<dependency><groupId>org.apache.tomcat</groupId><artifactId>jakartaee-migration</artifactId><version>1.0.0</version> </dependency><dependency><groupId>biz.aQute.bnd</groupId><artifactId>biz.aQute.bndlib</artifactId><version>5.3.0</version><scope>provided</scope> </dependency>在pom.xml右鍵,選擇Add as Maven project
在org.apache.catalina.startup.ContextConfig#contextConfig?方法中添加jsp解析,
context.addServletContainerInitializer(new JasperInitializer(), null);
因為tomcat源碼工程的webapps里面的工程都是沒有經過編譯的示例工程,將下載的經過編譯的二進制工程中的webapps和conf目錄覆蓋到tomcat源代碼工程中。并在jvm啟動參數中添加tomcat啟動參數,指定tomcat主目錄和配制文件目錄。
-Dcatalina.home=D:\projects\apache-tomcat-8.5.50-src\
-Dcatalina.base=D:\projects\apache-tomcat-8.5.50-src\
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=D:\projects\apache-tomcat-8.5.50-src/conf/logging.properties
Tomcat啟動入口在org.apache.catalina.startup.Bootstrap中的main函數中,啟動它。啟動成功后訪問127.0.0.1:8080 ,成功!
Tomcat請求分析:
1、org.apache.tomcat.util.net.NioEndpoint.SocketProcessor#doRun???--> ?接收Socket請求
org.apache.coyote.http11.Http11Processor#service--> ??封裝tomcat req 和res
2、org.apache.catalina.connector.CoyoteAdapter#service ???????????--> 將tomcat req 和res 轉換為ServletRequest 和ServletResponse
3、org.apache.catalina.core.StandardEngineValve#invoke????????????-->根據請求的服務器名稱,選擇合適的子主機來處理此請求。 如果找不到匹配的主機,則返回相應的 HTTP 錯誤。
4、org.apache.catalina.core.StandardHostValve#invoke??????????????-->根據Content部署的地址查找應用
5、org.apache.catalina.core.StandardContextValve#invoke???????????-->根據指定的請求 URI,選擇適當的子 Wrapper 來處理此請求。 如果找不到匹配的 Wrapper,則返回相應的 HTTP 錯誤。在進行Wrapper 查找之前先判斷路徑是有WEB-INF等路徑,這些路徑返回404
6、org.apache.catalina.core.StandardWrapperValve#invoke??????????-->調用正在管理的 servlet,遵守有關 servlet 生命周期和 SingleThreadModel 支持的規則。
7、org.apache.catalina.core.ApplicationFilterChain#doFilter??????????-->調用此鏈中的下一個過濾器,傳遞指定的請求和響應。 如果此鏈中沒有更多過濾器,則調用 servlet 本身的service()方法。
3? Tomcat性能調優
我們看一個服務器的性能指標主要是從兩個方面著手:
我們tomcat性能優化從兩個方面入手:
3.1?JVM內存模型及JVM內存參數調優
理解了JVM內存模型,我們才能更好的去對JVM內存調優。
本地方法棧:C++Native執行所需的棧區,為線程私有
程序計數器:保存程序執行的位置,為線程私有
棧:程序運行時方法的臨時變量保存區域,為線程私有,保存的也是線程的執行位置
堆:存儲對象
元數據區(方法區):靜態變量、方法、類加載器保存區域
1)JVM內存參數調優
我們對JVM內存優化主要是對堆內存進行調整,其中JVM內存參數如下表:
| 參數 | 說明 | 配置建議 |
| -server | 啟動Server,以服務端模式運? | 服務端模式建議開啟 |
| -Xms | 最?堆內存 | 建議與-Xmx設置相 |
| -Xmx | 最?堆內存 | 建議設置為可?內存的80%,這個可用內存是其他軟件使用完畢后還可以剩下使用的內存 |
| -XX:MetaspaceSize | 元空間初始值 | |
| - | 元空間最?內存 | 默認?限 |
| -XX:NewRatio | 年輕代和?年代???值,取值為整數,默 | 不需要修改 |
| -XX:SurvivorRatio | Eden區與Survivor區??的?值,取值為整 | 不需要修改 |
參數調整示例:
我的計算機是8G內存,開機后剩余內存5G,我以服務模仿是運行,最大堆內存和最小堆內存相同,占用5G * 80% = 4G,其他參數不需要調整。
JAVA_OPTS="-server -Xms4096m -Xmx4096m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
使用jmap 查看tomcat內存使用情況:
netstat -ano 找到tomcat的pid
輸入jmap -heap 8940 ,查看tomcat內存使用情況
| GC策略 | 說明 |
| 串行收集器 | 單線程執行所有的垃圾回收工作,適用于單核CPU服務器。工作進程--->同一個線程GC工作-->工作進程 |
| 并行收集器 | 以并行的方式執行年輕代的垃圾回收,可以降低垃圾回收的開銷。適用于多核處理器多線程的硬件上的數據量較大的應用。工作進程--->多個線程GC工作--->工作進程 |
| 并發收集器 | 并發的方式執行大部分垃圾回收工作,以縮短回收暫停時間。使用于那些響應時間優先于吞吐量的應用,因為該收集器縮短了暫停時間,但會降低應用程序性能。 |
| CMS收集器 | 并發標記清除收集器,使用于那些更愿意縮短垃圾回收暫停時間并負擔起與垃圾回收共享處理資源的應用。 |
| G1收集器 | 適用于大容量內存的多核服務器,可以在滿足垃圾回收暫停時間目標的同時,以最大可能性實現高吞吐量。該機制在JDK1.7之后才支持。 |
垃圾回收機制參數配置如下表所示:
| 參數 | 描述 |
| -XX:+UseSerialGC | 啟?串?收集器 |
| -XX:+UseParallelGC | 啟?并?垃圾收集器,配置了該選項,那么 -XX:+UseParallelOldGC默認啟? |
| -XX:+UseParNewGC | 年輕代采?并?收集器,如果設置了 -XX:+UseConcMarkSweepGC選項,?動啟? |
| -XX:ParallelGCThreads | 年輕代及?年代垃圾回收使?的線程數。默認值依賴于JVM使?的CPU個數 |
| -XX:+UseConcMarkSweepGC(CMS) | 對于?年代,啟?CMS垃圾收集器。 當并?收集器?法滿?應?的延遲需 |
| -XX:+UseG1GC | 啟?G1收集器。 G1是服務器類型的收集器, ?于多核、?內存的機器。它在保持?吞吐量的情況下,?概率滿?GC暫停時間的?標。 |
參數示例:
JAVA_OPTS="-XX:+UseConcMarkSweepGC"?
3.2 tomcat配置調優
- 在配置文件中,調整conf/server.xml,配置tomcat線程池
對性能影響的線程池參數如下表所示:
| 參數 | 說明 |
| maxConnections | 最?連接數,當到達該值后,服務器接收但不會處理更多的請求, 額外的請求將會阻塞直到連接數低于maxConnections 。可通過ulimit -a 查看服務器限制。對于CPU要求更?(計算密集型)時,建議不要配置過? ; 對于CPU要求不是特別?時,建議配置在2000左右(受服務器性能影響)。 當然這個需要服務器硬件的?持。 |
| maxThreads | 最?線程數,需要根據服務器的硬件情況,進??個合理的設置 |
| acceptCount | 最?排隊等待數,當服務器接收的請求數量到達maxConnections ,此時Tomcat會將后?的請求,存放在任務隊列中進?排序, acceptCount指的就是任務隊列中排隊等待的請求數 。?臺Tomcat的最?的請求處理數量,是maxConnections+acceptCount。 |
- 配置conf/server.xml,禁用AJP連接器
- 配置conf/server.xml,調整IO模式
????????Tomcat8之前默認使用BIO,每一個請求創建一個線程,不適用高并發;Tomcat8以后版本默認使用NIO。
????????Tomcat并發性能有較?要求或者出現瓶頸時,我們可以嘗試使?APR模式,?APR(Apache PortableRuntime)是從操作系統級別解決異步IO問題,使?時需要在操作系統上安裝APR和Native(因為APR原理是使?使?JNI技術調?操作系統底層的IO接?)?
- 動靜分離
可以使用Nginx+tomcat相結合,tomcat比較擅長處理動態資源,可以將靜態資源丟給Nginx處理。
總結
以上是生活随笔為你收集整理的Tomcat原理剖析及性能调优的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Leetcode][第1143题][J
- 下一篇: web开发常用js功能性小技巧(转)