Tomcat配置和優化 Tomcat配置和優化

Tomcat配置和優化

Tomcat 服務器是一個免費的開放源代碼的Web 應用服務器,屬於輕量級應用服務器,在中小型系統和併發訪問用戶不是很多的場合下被普遍使用,是開發和調試JSP 程序的首選。對於一個初學者來說,可以這樣認爲,當在一臺機器上配置好Apache 服務器,可利用它響應HTML(標準通用標記語言下的一個應用)頁面的訪問請求。實際上Tomcat是Apache 服務器的擴展,但運行時它是獨立運行的,所以當你運行tomcat 時,它實際上作爲一個與Apache 獨立的進程單獨運行的。

一、Tomcat Http Server


1. 部署JAVA環境
# tar xf jdk-8u91-linux-x64.tar.gz -C /usr/local
# ln -s /usr/local/jdk1.8.0_91 /usr/local/java

# vim /etc/profile.d/jdk.sh
JAVA_HOME=/usr/local/java
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME PATH

# source /etc/profile.d/jdk.sh
# env |grep JAVA
JAVA_HOME=/usr/local/java
# java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

2. 安裝Tomcat:
# tar xf apache-tomcat-7.0.34.tar.gz -C /usr/local/
# ln -s /usr/local/apache-tomcat-7.0.34 /usr/local/tomcat

定義Tomcat所需環境變量:
# vim /etc/profile.d/jdk.sh //定義Tomcat環境變量
CATALINA_HOME=/usr/local/tomcat //Tomcat安裝目錄
PATH=$CATALINA_HOME/bin:$PATH
export CATALINA_HOME PATH
# source /etc/profile.d/jdk.sh

啓動Tomcat
# /usr/local/tomcat/bin/startup.sh //啓動tomcat
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/java/jdk1.7.0_11
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.

# netstat -tnlp |grep java
tcp 0 0 ::ffff:127.0.0.1:8005 :::* LISTEN 6191/java
tcp 0 0 :::8009 :::* LISTEN 6191/java
tcp 0 0 :::8080 :::* LISTEN 6191/java

# /usr/local/tomcat/bin/shutdown.sh //關閉tomcat

關於tomcat端口:
Tomcat服務器通過Connector連接器組件與客戶程序建立連接,Connector組件負責接收客戶的請求,以及把Tomcat服務器的響應結果發送給客戶。默認情況下,Tomcat在server.xml中配置了兩種連接器:
  第一個連接器監聽8080端口,負責建立HTTP連接。在通過瀏覽器訪問Tomcat服務器的Web應用時,使用的就是這個連接器。  
  第二個連接器監聽8009端口,負責和其他的HTTP服務器建立連接。在把Tomcat與其他HTTP服務器集成時,就需要用到這個連接器。

8005是tomcat本身的端口

3. 測試
http://192.168.1.6:8080/

 

二. Tomcat多實例子部署

多實例運行不同的應用(類似虛擬主機)
多實例運行相同的應用(實現負載均衡,支持高併發處理)

(1) 配置多實例目錄

分別在/usr/local/tomcat下創建目錄instance1 instance2 instance3,拷貝原來的conf,logs,temp,work到3個目錄下

(2) 配置實例server.xml

<Server port="****" 管理實例端口
<Connector port="****" protocol="HTTP/1.1 提供web服務端口
<Connector port="****" protocol="AJP/1.3" 用於前端如Apache通過AJP方式連接tomcat的端口



[root@www ~]# vim /usr/local/tomcat/instance1/conf/server.xml

第一個修改點:22 <Server port="8015" shutdown="SHUTDOWN"> 修改爲8015,8025,8035.......

第二個修改點:71 <Connector port="8081" protocol="HTTP/1.1" 修改爲8081,8082,8083...........
connectionTimeout="20000"
redirectPort="8443" />

第三個修改點:93 <Connector port="8019" protocol="AJP/1.3" redirectPort="8443" /> 修改爲8019,8029,8039........

第四個修改點:125 <Host name="localhost" appBase="/webapps1" //修改網站基準目錄
unpackWARs="true" autoDeploy="true">

(3). 腳本內容(多實例啓動腳本)
[root@www ~]# vim /usr/local/tomcat/instance1/ins1.sh

#!/bin/bash
#instance1
. /etc/init.d/functions
export CATALINA_BASE="/usr/local/tomcat/instance1"

case "$1" in
start)
$CATALINA_HOME/bin/startup.sh
;;
stop)
$CATALINA_HOME/bin/shutdown.sh
;;
restart)
$CATALINA_HOME/bin/shutdown.sh
sleep 3
$CATALINA_HOME/bin/startup.sh
;;
esac

(4). 啓動測試
[root@www ~]# /usr/local/tomcat/instance1/ins1.sh start
[root@www ~]# /usr/local/tomcat/instance2/ins2.sh start
[root@www ~]# /usr/local/tomcat/instance3/ins3.sh start

[root@tomcat instance3]# ss -tnlp |grep :80 |column -t
LISTEN  0  1    ::ffff:127.0.0.1:8015  :::*  users:(("java",pid=2940,fd=46))
LISTEN  0  100  :::8080                :::*  users:(("java",pid=2733,fd=42))
LISTEN  0  100  :::8081                :::*  users:(("java",pid=2940,fd=42))
LISTEN  0  100  :::8082                :::*  users:(("java",pid=2905,fd=42))
LISTEN  0  100  :::8083                :::*  users:(("java",pid=2999,fd=42))
LISTEN  0  100  :::8019                :::*  users:(("java",pid=2940,fd=43))
LISTEN  0  1    ::ffff:127.0.0.1:8025  :::*  users:(("java",pid=2905,fd=46))
LISTEN  0  100  :::8029                :::*  users:(("java",pid=2905,fd=43))
LISTEN  0  1    ::ffff:127.0.0.1:8035  :::*  users:(("java",pid=2999,fd=48))
LISTEN  0  1    ::ffff:127.0.0.1:8005  :::*  users:(("java",pid=2733,fd=46))
LISTEN  0  100  :::8039                :::*  users:(("java",pid=2999,fd=43))
LISTEN  0  100  :::8009                :::*  users:(("java",pid=2733,fd=43))
 

三. Tomcat負載均衡:

採用Nginx作爲反向代理服務器實現Tomcat負載均衡:

配置Nginx:

1. 停止原有web服務器:
2. 添加普通用戶賬號來運行nginx:
# useradd -M -s /sbin/nologin nginx
3. 安裝依賴,解壓並安裝Nginx:
#yum -y install zlib zlib-devel openssl openssl-devel pcre pcre-devel gcc gcc-c++ make cmake
# tar xf nginx-1.10.3.tar.gz
# cd nginx-1.10.3
# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_stub_status_module --with-http_ssl_module
# make && make install

4. 啓動:

[root@tomcat sbin]# vim /etc/profile
export NGINX_HOME=/usr/local/nginx
export PATH=$NGINX_HOME/sbin:$PATH
[root@tomcat sbin]# source /etc/profile
[root@tomcat sbin]# nginx

5. 測試:

[root@tomcat sbin]# netstat -tanp | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 5614/nginx: master

#curl 127.0.0.1

6. 配置負載均衡:

[root@www ~]# vim /usr/local/nginx/conf/nginx.conf
http {
upstream tomcat_pool {                                         //輪詢方式
server 192.168.1.6:8081;
server 192.168.1.6:8082;
server 192.168.1.6:8083;
}

server {
location / {
proxy_pass http://tomcat_pool;
proxy_redirect off; 開啓代理重定向
proxy_set_header Host $host; 開啓使用遠程主機
proxy_set_header X-Real-IP $remote_addr; 獲取遠程主機IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 使用代理主機轉發地址
}
}
}

在三個tomcat發佈目錄/webapp1/ROOT/中寫入對應IP,curl本地迴環後出現輪詢的負載均衡效果:

[root@tomcat ROOT]# curl 127.0.0.1
192.168.1.6:8081
[root@tomcat ROOT]# curl 127.0.0.1
192.168.1.6:8082
[root@tomcat ROOT]# curl 127.0.0.1
192.168.1.6:8083
 

四. 優化:

tomcat調優主要從以下三方面着手:
1. 內存優化
2. 併發優化
3. 內核優化


1. JVM內存優化

再講解JVM內存優化之前,需要大家知道什麼是JAVA堆。

首先:Java把內存劃分成兩種:一種是棧內存,一種是堆內存。

其中Java堆是Java虛擬機所管理的內存中最大的一塊。Java堆是指:在JAVA中被所有線程所共享的一塊內存區域,在虛擬機啓動時創建。
此內存區域的唯一目的就是存儲對象實例,幾乎所有的對象實例都在這裏分配內存。

內存溢出的處理辦法(tomcat內存優化):
Tomcat內存優化主要是對 tomcat 啓動參數優化.
Tomcat默認使用最大內存爲128M,可以修改 tomcat/bin/Catalina.sh 文件增加其內存。(在此文件第一行開始添加新的設置即可)設置 JAVA_OPTS 參數


JAVA_OPTS=”-server -Xms1024m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m -Djava.awt.headless=true”

-server  以服務端模式啓動,啓動速度會稍微慢點,但性能會高很多.;  
-Xms    java虛擬機初始化時的初始堆大小;  
-Xmx   java虛擬機可使用的最大堆大小;  
-XX:PermSize     內存永久保留區域  
-XX:MaxPermSize   內存最大永久保留區域(老生代對象能佔用內存的最大值)  
java.awt.headless=true 與圖形操作有關,適用於Linux系統

注:要加“m”說明是MB,否則就是KB,在啓動tomcat時會報內存不足。
注:永久保存區:是指內存的永久保存區域。這一部分用於存放Class(類)和Meta(+類)的信息,Class在被 Load(加載)的時候被放入PermGen space區域(內存永久保存區域),並且不會在主程序運行期對內存永久保存區進行清理,所以如果你的APP會LOAD很多CLASS的話,就很可能出現PermGen space錯誤。這種錯誤常見在web服務器對JSP進行pre compile(預編譯)的時候。jvm的gc是不會清理PemGen space的,導致內存溢出
==================================================================

常見的內存溢出一般會有下面三種情況:
        1.OutOfMemoryError: Java heap space #堆溢出,若不是程序邏輯bug,可能是項目中引用的jar較多導致
        2.OutOfMemoryError: PermGen space #永久保存區域溢出,通常由於加載了過多的類導致
        3.OutOfMemoryError: unable to create new native thread. #當JVM的heap size(堆空間)設置過大時,thread(線程)的創建數量便會減少
解決:
1.調整-Xms -Xmx參數可以解決第一種情況

64位操作系統對堆內存大小沒有限制
堆的大小可以使用 java -Xmx***M  -version 命令來測試,***位置填寫想設置的內存大小。支持的話會出現jdk的版本號,不支持會報錯。

    -Xms -Xmx一般配置成一樣比較好比如set JAVA_OPTS= -Xms1024m -Xmx1024m
   注意:java -Xmx***M  version 命令來測試的最大堆內存是 -Xmx與 -XX:PermSize的和。
   
2.加大-XX:PermSize  -XX:MaxPermSize來解決第二種情況  
3.解決問題3方法如下:
  1).如果程序中有bug,導致創建大量不需要的線程或者線程沒有及時回收,那麼必須解決這個bug,修改參數是不能解決問題的。
  2).如果程序確實需要大量的線程,現有的設置不能達到要求,那麼可以通過修改MaxProcessMemory(內存最大永久保留區),JVMMemory(JVM內存),ThreadStackSize(線程棧大小)這三個因素,來增加能創建的線程數:
如下方式解決:
  a, MaxProcessMemory 使用64位操作系統
  b, JVMMemory   減少JVMMemory的分配  使用tomcat的catalina.sh這裏配置,JAVA_OPTS=-Xms1024m -Xmx1024m -XX:PermSize=256M -XX:MaxNewSize=512m-XX:MaxPermSize=256m
  c, ThreadStackSize  減小單個線程的棧大小 

這個異常問題本質原因是:
我們創建了太多的線程,而能創建的線程數是有限制的,導致了異常的發生。
能創建的線程數的具體計算公式如下: 
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
一個進程的最大內存——JVm內存——操作系統預留內存)/線程棧大小=線程數
在java語言裏, 當你創建一個線程的時候,虛擬機會在JVM內存創建一個Thread(線程)對象同時創建一個操作系統線程,而這個系統線程的內存用的不是JVMMemory,而是系統中剩下的內存(MaxProcessMemory - JVMMemory - ReservedOsMemory)。由公式得出結論:你給JVM內存越多,那麼你能創建的線程越少,越容易發生java.lang.OutOfMemoryError: unable to create new native thread。

2. 併發優化
線程池設置:
線程池指定Web請求負載的數量,因此,爲獲得更好的性能這部分應小心處理。可以通過調整連接器屬性“maxThreads”完成設置。maxThreads的值應該根據流量的大小,如果值過低,將有沒有足夠的線程來處理所有的請求,請求將進入等待狀態,只有當一個的處理線程釋放後才被處理;如果設置的太大,Tomcat的啓動將花費更多時間。因此併發優化好壞取決於我們是否給maxThreads設置了一個正確的值。
#vim server.xml
<Connector port="8080" protocol="HTTP/1.1"
maxThreads="500"
connectionTimeout="20000"
redirectPort="8443" />

在上述配置中,maxThreads值設定爲“500”,指定可以由服務器處理的併發請求的最大數量是500。如果沒有指定,這個屬性的默認值爲“200”。任何多出的併發請求將收到“拒絕連接”的錯誤提示,直到另一個處理請求進程被釋放。錯誤看起來如下,
[ org.apache.tomcat.util.threads.ThreadPool logFull SEVERE: All threads (500) are  currently busy, waiting. Increase maxThreads (500) or check the servlet status  

最好使用“Tomcat集羣”的多個實例。也就是說,如果有“1000”請求,兩個Tomcat實例設置“maxThreads= 500”,而不在單Tomcat實例的情況下設置maxThreads=1000。

-----------------------------
<Connector port="9027"      
                protocol="HTTP/1.1"     
                maxHttpHeaderSize="8192"     
 maxThreads="1000"      客戶請求最大線程數
                minSpareThreads="100"      Tomcat初始化時創建的 socket 線程數
                maxSpareThreads="1000"   Tomcat連接器的最大空閒 socket 線程數   
                minProcessors="100"     服務器創建時的最小處理線程數
                maxProcessors="1000"    服務器同時最大處理線程數
                enableLookups="false"      若設爲true, 則支持域名解析,可把 ip 地址解析爲主機名
                URIEncoding="utf-8"     URL統一編碼
                acceptCount="1000"      監聽端口隊列最大數,滿了之後客戶請求會被拒絕(不能小於maxSpareThreads  )
                redirectPort="8443"      在需要基於安全通道的場合,把客戶請求轉發到基於SSL 的 redirectPort 端口
                disableUploadTimeout="true"/> 

3. 內核優化
Tomcat運行過程中可能會出現大量ESTABLISHED(已建立的)連接與Time_Wait(等待)連接
# netstat -n | awk '/^tcp/ {++y[$NF]} END {for(w in y) print w, y[w]}'
CLOSE_WAIT 348 已關閉的等待鏈接
ESTABLISHED 1240 已建立的鏈接
TIME_WAIT 5621 等待連接,也是監控web與tomcat之間的鏈接端口
#netstat -n | grep 8009 | wc -l
7198
1、怎樣解決time_wait過多的問題:
通過調整內核參數:
# vim /etc/sysctl.conf #編輯文件,加入以下內容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
# /sbin/sysctl -p #讓參數生效。
配置說明:
net.ipv4.tcp_syncookies = 1 表示開啓SYN Cookies。當出現SYN等待隊列溢出時,啓用cookies來處理,可防範少量SYN攻擊,默認爲0,表示關閉;
net.ipv4.tcp_tw_reuse = 1    表示開啓重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認爲0,表示關閉;
net.ipv4.tcp_tw_recycle = 1  表示開啓TCP連接中TIME-WAIT sockets的快速回收,默認爲0,表示關閉;
net.ipv4.tcp_fin_timeout=30修改系統默認的 TIMEOUT 時間。

如果以上配置調優後性能還不理想,可繼續修改一下配置:
# vi /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 1200
#表示當keepalive起用的時候,TCP發送keepalive消息的頻度。缺省是2小時,改爲20分鐘。keepalive,是在TCP中一個可以檢測死連接的機制。
net.ipv4.ip_local_port_range = 1024 65000
#表示用於向外連接的端口範圍。缺省情況下很小:32768到61000,可以改爲1024到65000。
net.ipv4.tcp_max_syn_backlog = 8192
#表示SYN隊列的長度,默認爲1024,加大隊列長度爲8192,可以容納更多等待連接的網絡連接數。
net.ipv4.tcp_max_tw_buckets = 5000
#表示系統同時保持TIME_WAIT套接字的最大數量,如果超過這個數字,TIME_WAIT套接字將立刻被清除並打印警告信息。
默認爲180000,改爲5000。對於Apache、Nginx等服務器,上幾行的參數可以很好地減少TIME_WAIT套接字數量,但是對於 Squid,效果卻不大。此項參數可以控制TIME_WAIT套接字的最大數量,避免Squid服務器被大量的TIME_WAIT套接字拖死。
調優完畢,再壓一下看看效果:
# netstat -n | awk '/^tcp/ {++y[$NF]} END {for(w in y) print w, y[w]}'
ESTABLISHED 968


2、怎麼解決請求結束後依然存在大量ESTABLISHED沒有被釋放
初步推斷是tomcat服務器回收session時出了問題,這個一般都跟服務器的Timeout設置有聯繫。
查看tomcat的配置文件 server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"/>
*****檢查配置得出鏈接的超時時間爲20000毫秒的時候acceptCount(承受鏈接數統計)=”100” ,明顯不合理,連接數也太小了。
所以進一步優化:
connectionTimeout="20000" 改爲 connectionTimeout="100"
acceptCount="100"改爲acceptCount="5000"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章