Tomcat指南

安装

官网下载安装。

解压缩安装包后,看到如下路径:

06tNc79gy1fpgtg7bg4bj30xi0iuaa

  • bin:存放启动和关闭Tomcat相关的命令路径;
  • conf:存放Tomcat的配置。
  • lib:Tomcat服务器核心类库(JAR文件),若需要扩展功能,将第三方库类库复制到该目录下。
  • logs:起初是个空路径,记录Tomcat每次运行后的日志。
  • temp:保存Web应用运行过程中生成的临时文件。
  • webapps:用于自动部署Web应用,将Web应用复制到该路径下,Tomcat会将该应用自动部署在容器中。
  • work:保存Web应用在运行过程中,编译生成的class文件,该文件可以删除,每次启动Tomcat服务器是,系统会再次生成。
  • LICENSE相关文档。

日志配置

  1. 路径:/conf/logging.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#可配置项(5类日志):catalina、localhost、manager、admin、host-manager
handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler,
3manager.org.apache.juli.FileHandler, 4host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

#日志输出为输出到文件和输出到控制台
.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

#日志输出级别:SEVERE (最高级别) > WARNING > INFO > CONFIG > FINE > FINER(精心) > FINEST (所有内容,最低级别)
#配置文件使catalina日志输出级别为FINE
1catalina.org.apache.juli.FileHandler.level = FINE
#catalina文件输出位置
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
#catalina日志前缀为catalina
1catalina.org.apache.juli.FileHandler.prefix = catalina.

#配置文件使localhost日志输出级别为FINE
2localhost.org.apache.juli.FileHandler.level = FINE
#localhost文件输出位置
2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
#localhost日志前缀为localhost
2localhost.org.apache.juli.FileHandler.prefix = localhost.

#配置文件使manager日志输出级别为FINE
3manager.org.apache.juli.FileHandler.level = FINE
#manager文件输出位置
3manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
#manager日志前缀为manager
3manager.org.apache.juli.FileHandler.prefix = manager.

#配置文件使host-manager日志输出级别为FINE
4host-manager.org.apache.juli.FileHandler.level = FINE
#host-manager文件输出位置
4host-manager.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
#host-manager日志前缀为host-manager
4host-manager.org.apache.juli.FileHandler.prefix = host-manager.

#配置文件使控制台日志输出级别为FINE
java.util.logging.ConsoleHandler.level = FINE
#控制台日志输出格式
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

#localhost日志文件输出级别为INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
#localhost日志文件输出处理类2localhost.org.apache.juli.FileHandler
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.FileHandler

#manager日志文件输出级别为INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
#manager日志文件输出处理类3manager.org.apache.juli.FileHandler
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.FileHandler

#host-manager日志文件输出级别为INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
#host-manager日志文件输出处理类4host-manager.org.apache.juli.FileHandler
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.FileHandler
  1. app日志输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Service name="BeeFun">
<Connector port="8082" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="BeeFun" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="beefunapps"
unpackWARs="true" autoDeploy="true">
//日志设置
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%t [%I] %{X-Forwarded-For}i %a %r %s %D" />
</Host>
</Engine>
</Service>

参考:Logging in Tomcat

Server.xml配置

字段 默认值 说明
maxHttpHeaderSize HTTP请求和响应头的最大量,以字节为单位,默认值为4096字节

示例:

1
2
3
<Connector 
port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false"
redirectPort="8443" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" />

异常关闭

一般来说,tomcat自动关闭服务的原因,有如下这些:

  1. 并发用户数目过大,也会导致tomcat自动停止服务.

  2. 系统本身的网络负载平衡没有做好,导致tomcat自动停止服务.

  3. 程序迭代不合理也是一个原因.

  4. 数据库连接未关闭,导致资源损耗过重,会引起服务停止.

  5. 程序严重错误,也会引起tomcat停止服务.

  6. 内存溢出

内存溢出

JVM内存溢出

在Tomcat 目录下catalina.sh文件中设置JAVA_OPTS参数,放在首行。

1
JAVA_OPTS="-server -Xms512M -Xmx2048M -Xss1024k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=512M -XX:MaxPermSize=2048M "

若启动Tomcat时,出现以下警告 -XX:PermSize=512M -XX:MaxPermSize=2048M在JDK 8.0就被移除了:

1
2
3
4
5
6
7
8
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=512M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=2048M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000080000000, 715784192, 0) failed; error='Cannot allocate memory' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 715784192 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /root/hs_err_pid3178.log

两个问题:

  1. PermSize/MaxPermSize不适用了。用MaxMetaspaceSize/MetaspaceSize代替。
  2. 内存分配不足。

最后将上面调整修改为,具体看服务器配置:

1
JAVA_OPTS="-server -Xms512M -Xmx2048M -Xss256K"

相关配置:

堆设置
-server:一定要作为第一个参数,在多个CPU时性能佳
-Xms:初始堆内存大小,Server端JVM最好将-Xms和-Xmx设为相同值
-Xmx:堆内存最大值,建议不要超过物理内存的一半
-Xmn:年轻代堆内存的大小,一般设置为Xmx的三分之一
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:PermSize:设定内存永久保存区域的最小值
-XX:MaxPermSize=n:设定内存永久保存区域的最大值
-Xss 15120 每个线程的Stack大小,这使得JBoss每增加一个线程(thread)就会立即消耗15M内存,而最佳值应该是128K,默认值好像是512k.
+XX:AggressiveHeap 会使得 Xms没有意义。这个参数让jvm忽略Xmx参数,疯狂地吃完一个G物理内存,再吃尽一个G的swap。
-Xss:
-verbose:gc 现实垃圾收集信息
-Xloggc:gc.log 指定垃圾收集日志文件
-XX:+UseParNewGC :缩短minor收集的时间
-XX:+UseConcMarkSweepGC :缩短major收集的时间
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

常用配置
set JAVA_OPTS=-server -Xms2048M -Xmx2048M -Xss2048k -XX:+UseBiasedLocking -XX:PermSize=512M -XX:MaxPermSize=512M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=15 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true

Tomcat内存溢出

增大maxHttpHeaderSize,默认为4096字节。

1
2
3
<Connector 
port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false"
redirectPort="8443" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" />

守护进程

JSVC文件

1
2
3
4
5
6
7
8
9
10
root@106:~# cd/root/Open/apache-tomcat-8.5.31/bin
root@106:~/Open/apache-tomcat-8.5.31/bin# chmod 775 -R commons-daemon-1.1.0-native-src/
root@106:~/Open/apache-tomcat-8.5.31/bin# cd commons-daemon-1.1.0-native-src/unix/
root@106:~/Open/apache-tomcat-8.5.31/bin/commons-daemon-1.1.0-native-src/unix# configure
root@106:~/Open/apache-tomcat-8.5.31/bin/commons-daemon-1.1.0-native-src/unix# make

.....
gcc jsvc-unix.o libservice.a -ldl -lpthread -o ../jsvc
make[1]: Leaving directory '/root/Open/apache-tomcat-8.5.31/bin/commons-daemon-1.1.0-native-src/unix/native'

上述步骤之后,会在unix目录下创建jsvc,并将其复制到tomcat/bin目录下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@106:~/Open/apache-tomcat-8.5.31/bin/commons-daemon-1.1.0-native-src/unix# ls -l
total 428
-rw-r--r-- 1 root root 16107 Jun 7 12:24 config.log
-rwxr-xr-x 1 root root 58 Jun 7 12:24 config.nice
-rwxr-xr-x 1 root root 24779 Jun 7 12:24 config.status
-rwxrwxr-x 1 root root 147330 Jun 7 12:06 configure
-rwxrwxr-x 1 root root 5105 Nov 29 2016 configure.in
-rwxrwxr-x 1 root root 2602 Nov 29 2016 INSTALL.txt
-rwxr-xr-x 1 root root 199704 Jun 7 12:25 jsvc
-rw-r--r-- 1 root root 1193 Jun 7 12:24 Makedefs
-rwxrwxr-x 1 root root 1081 Jul 8 2017 Makedefs.in
-rw-r--r-- 1 root root 1110 Jun 7 12:24 Makefile
-rwxrwxr-x 1 root root 1110 Jul 10 2017 Makefile.in
drwxrwxr-x 2 root root 4096 Nov 15 2017 man
drwxrwxr-x 2 root root 4096 Jun 7 12:25 native
drwxrwxr-x 2 root root 4096 Nov 15 2017 support

root@106:~/Open/apache-tomcat-8.5.31/bin/commons-daemon-1.1.0-native-src/unix# cp jsvc ../../../bin/

daemon.sh

更改用户

1
2
3
4
5
test ".$TOMCAT_USER" = . && TOMCAT_USER=tomcat
# Set JAVA_HOME to working JDK or JRE
# JAVA_HOME=/opt/jdk-1.6.0.22
# If not set we'll try to guess the JAVA_HOME
# from java binary if on the PATH

将上面这段修改为用户root,JAVA_HOME如果配置了,在这里可以不用设置:

1
2
3
4
5
test ".$TOMCAT_USER" = . && TOMCAT_USER=root
# Set JAVA_HOME to working JDK or JRE
# JAVA_HOME=/opt/jdk-1.6.0.22
# If not set we'll try to guess the JAVA_HOME
# from java binary if on the PATH

启动

1
2
root@106:~# /root/Open/apache-tomcat-8.5.31/bin/daemon.sh start
root@106:~# /root/Open/apache-tomcat-8.5.31/bin/daemon.sh stop

添加成服务

将daemon.sh添加成为Ubuntu的服务就非常简单了,只需要创建一个软链到/etc/init.d/目录中即可:

1
root@106:~# ln -s /root/Open/apache-tomcat-8.5.31/bin/daemon.sh /etc/init.d/tomcat8                

之后可以用服务来启动:

1
2
/etc/init.d/tomcat8 start
/etc/init.d/tomcat8 stop

开机自启动

1
2
3
4
# 增加自启动
root@106:~# update-rc.d tomcat8 defaults
# 移除自启动
root@106:~# update-rc.d tomcat8 remove

可以通过工具sysv-rc-conf查看:

1
sudo sysv-rc-conf

如下图:

性能调优

压缩优化

1
2
3
4
<Connector port="80" maxHttpHeaderSize="8192" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
compressableMimeType="text/html,text/xml,text/plain,application/octet-stream" />

运行模式

tomcat的运行模式有3种:

bio 默认的模式,性能非常低下,没有经过任何优化处理和支持.
nio nio(new I/O),是Java SE 1.4及后续版本提供的一种新的I/O操作方式(即java.nio包及其子包)。Java nio是一个基于缓冲区、并能提供非阻塞I/O操作的Java API,因此nio也被看成是non-blocking I/O的缩写。它拥有比传统I/O操作(bio)更好的并发运行性能。
apr 安装起来最困难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能.

安装APR

在apache/webapps/doc下,找到apr.html:

Linux

Most Linux distributions will ship packages for APR and OpenSSL. The JNI wrapper (libtcnative) will then have to be compiled. It depends on APR, OpenSSL, and the Java headers.

Requirements:

  • APR 1.2+ development headers (libapr1-dev package)
  • OpenSSL 1.0.2+ development headers (libssl-dev package)
  • JNI headers from Java compatible JDK 1.4+
  • GNU development environment (gcc, make)

The wrapper library sources are located in the Tomcat binary bundle, in the bin/tomcat-native.tar.gz archive. Once the build environment is installed and the source archive is extracted, the wrapper library can be compiled using (from the folder containing the configure script):

1
./configure && make && make install

要安装上面所有依赖包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@106:~# sudo apt-get install libapr1-dev libaprutil1-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

#错误1:
The following packages have unmet dependencies:
libapr1-dev : Depends: uuid-dev but it is not going to be installed
E: Unable to correct problems, you have held broken packages.

上面说uuid-dev有问题,那么就再重新:

1
2
3
4
5
6
7
8
9
10
11
12
13
root@106:~# sudo apt-get install uuid-dev                                                 
Reading package lists... Done
Building dependency tree
Reading state information... Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
uuid-dev : Depends: libuuid1 (= 2.27.1-6ubuntu3.4) but 2.27.1-6ubuntu3.5 is to be installed
E: Unable to correct problems, you have held broken packages

这下找到问题了,就根据旧版本安装,作降级:

1
root@106:~# sudo apt-get install libuuid1=2.27.1-6ubuntu3.4

开启APR

1
2
3
4
<Server port="8005" shutdown="SHUTDOWN">
<!--APR library loader. Documentation at /docs/apr.html -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
</Server>

上面开关SSLEngine on/off即可。

错误

  1. org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    org.apache.coyote.http11.Http11Processor.service Error parsing HTTP request header
    Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
    java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
    at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:428)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:687)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

    错误原因:本是http的请求,错误使用https方式请求。

    解决:确保客户端采用的是http请求。

    参考:「1」

参考

性能调优部分参考:Tomcat 生产服务器性能优化 原文