1、隐藏版本信息
隐藏 HTTP 头部的版本信息
# 为 Connector 添加 server 属性
vim /usr/local/tomcat/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" server="web/8.8.8"/>
# 重启 tomcat 服务
systemctl restart tomcat
# 测试
curl -I 192.168.137.253:8080
隐藏 404 页面出现的版本号
# 进入 tomcat 的 lib 目录找到 catalina.jar 文件
cd /usr/local/tomcat/lib/
# 解压 catalina.jar 包
unzip catalina.jar | jar -xvf catalina.jar
ls
# 修改 ServerInfo.properties 文件
vim org/apache/catalina/util/ServerInfo.properties
server.info=web/8.8.8
server.number=8.8.8.8
server.built=Jan 5 2024 15:56:27 UTC
# 将修改后的信息压缩回 jar 包
jar uvf catalina.jar org/apache/catalina/util/ServerInfo.properties
# 删除解压目录
rm -rf META-INF/ org/
# 测试
http://192.168.137.253:8080/abc.jsp
2、运行模式的优化
BIO(阻塞式)
Tomcat 的默认模式,该模式性能较低,没有经过任何优化处理和支持,一个线程处理一个请求。缺点:并发量过大时,线程数较多,浪费资源。Tomcat7 及以下版本,在 linux 系统中默认使用该模式。
NIO(非阻塞 IO)
Java NIO 可以让你非阻塞的使用 IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。Tomcat8 在 Linux 系统中默认使用这种方式。 不过,在 tomcat8 中有最新的 nio2,速度更快,建议使用 nio2.
# 修改配置文件
vim /usr/local/tomcat/conf/server.xml #修改标红地方
<Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000" redirectPort="8443" server="web/8.8.8"/>
# 重启 tomcat
systemctl restart tomcat
# 查看日志
cat /usr/local/tomcat/logs/catalina.out | grep nio2
APR
APR(Apache Portable Runtime/Apache 可移植运行时),是 Apache HTTP 服务器的支持库。你可以简单地理解为,Tomcat 将以 JNI 的形式调用 Apache HTTP 服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高 Tomcat 对静态文件的处理性能。apache apr 也是在 Tomcat 上运行高并发应用的首选模式。从操作系统级别来解决异步的 IO 问题,大幅度的提高性能。必须要安装 apr 和 native,直接启动就支持 apr。
Tomcat 可以使用 apr 来提供更好的伸缩性、性能和集成到本地服务器技术。用来提高 tomcat 的性能。 tomcat native 在具体的运行平台上,提供了一种优化技术,它本身是基于 APR(Apache Portable(轻便) Runtime)技术,我们应用了 tomcatnative 技术之后,tomcat 在跟操作系统级别的交互方面可以做得更好,并且它更像 apache 一样,可以更好地作为一台 web server。 tomcat 可以利用 apache 的 apr 接口,使用操作系统的部分本地操作,从而提升性能 APR 提升的是静态页面处理能力。
# 安装 apr、apr-devel
yum install -y apr apr-devel gcc gcc-c++ openssl-devel openssl
ls /usr/local/tomcat/bin/
tar zxf /usr/local/tomcat/bin/tomcat-native.tar.gz -C /usr/local/src/
cd /usr/local/src/tomcat-native-1.2.21-src/native/
./configure --with-apr=/usr/bin/apr-1-config --with-java-home=/usr/local/jdk1.8.0_171/ --with-ssl
make && make install
# 添加软连接
ln -s /usr/local/apr/lib/* /usr/lib/
# 在 conf/server.xml 中修改 8080 端口对应的 Connector
vim /usr/local/tomcat/conf/server.xml
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
connectionTimeout="20000" redirectPort="8443" server="web/8.8.8"/>
# 重启 tomcat
systemctl restart tomcat
# 看日志是否支持 native
cat /usr/local/tomcat/logs/catalina.out | grep Native
# 查看日志是否支持 apr
cat /usr/local/tomcat/logs/catalina.out | grep apr
3、隐藏目录结构
vim /usr/local/tomcat/conf/web.xml
:默认 tomcat 就是隐层目录结构的。
4、线程池的优化
Tomcat 默认是没有启用线程池的,在 Tomcat 中每一个用户请求都是一个线程,所以我们可以使用线程池来提高性能。Tomcat 的前端有一个调度线程,会将用户的请求放入线程池中,一定时间后线程池中的用户请求任务就变为工作线程。 使用线程池,用较少的线程处理较多的访问,可以提高 tomcat 处理请求的能力。
# 开启线程池
vim /usr/local/tomcat/conf/server.xml
# 在 <Service name="Catalina"> 里面添加:
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="1500" minSpareThreads="100" maxSpareThreads="500"
prestartminSpareThreads="true" maxQueueSize="300" />
参数 | 说明 |
name | 共享线程池的名字。这是 Connector 为了共享线程池要引用的名字,该名字必须唯一。 |
namePrefix | 在 JVM 上,每个运行线程都可以有一个 name 字符串。这一属性为线程池中每个线程的 name 字符串设置了一个前缀,Tomcat 将把线程号追加到这 一前缀的后面。 |
maxThreads | 最大并发数,默认设置 200,一般建议在 500 ~1000,根据硬件设施和业务来判断。 |
minSpareThreads | 最小空闲线程数,Tomcat 初始化时创建的线程数,默认设置 25 |
maxSpareThreads | 最大空闲线程数,一旦空闲线程超过这个值,Tomcat 就会关闭 不再需要的线程。 |
prestartminSpareThreads | 在 Tomcat 初始化的时候就初始化minSpareThreads 的参数值,如果不等于 true,minSpareThreads 的值就没啥效果了 |
maxQueueSize | 最大的等待队列数,超过则拒绝请求 |
# 使用线程池:在 connector 中设置 executor属性指向上面的执行器
vim /usr/local/tomcat/conf/server.xml
<Connector executor="tomcatThreadPool" port="8080"
protocol="org.apache.coyote.http11.Http11AprProtocol" connectionTimeout="20000"
redirectPort="8443" server="web/8.8.8"/>
5、连接器的优化
Connector 是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以 Connector 的优化是重要部分。默认情况下 Tomcat 支持 200 线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力。
vim /usr/local/tomcat/conf/server.xml
<Connector executor="tomcatThreadPool" port="8080"
protocol="org.apache.coyote.http11.Http11AprProtocol"
connectionTimeout="20000"
redirectPort="8443" server="web/8.8.8"
maxThreads="1000"
minSpareThreads="100"
acceptCount="1000"
maxConnections="1000"
maxHttpHeaderSize="8192"
tcpNoDelay="true"
compression="on"
disableUploadTimeout="true"
enableLookups="false"
URIEncoding="UTF-8"/>
参数 | 说明 |
maxThreads | 最大线程数。即最多同时处理的连接数,Tomcat 使用线程来处理接收的每个请求。这个值表示 Tomcat 可创建的最大的线程数。如果没有指定,该属性被设置为 200。如果使用了 executor 将忽略此连接器的该属性,连接器将使用 executor。 |
minSpareThreads | 最小空闲线程数。 |
acceptCount | 接受最大队列长度,当队列满时收到的任何请求将被拒绝。 |
maxConnections | 在任何给定的时间服务器接受并处理的最大连接数。 |
connectionTimeout | 超时等待时间(毫秒) |
maxHttpHeaderSize | 请求头最大值 |
tcpNoDelay | 如果为 true,服务器 socket 会设置 TCP_NO_DELAY 选项,在大多数情况下可以提高性能。缺省情况下设为 true |
compression | 是否开启压缩 GZIP 。可接受的参数的值是“off ”(禁用压缩),“on ” (允许压缩,这会导致文本数据被压缩),“force ”(强制在所有的情况下压缩)。 提示:压缩会增加 Tomcat 负担,最好采用 Nginx + Tomcat 或者 Apache + Tomcat方式,压缩交由 Nginx/Apache 去做。 |
disableUploadTimeout | 此标志允许 servlet 容器在数据上传时使用不同的连接超时,通常较长。如果没有指定,该属性被设置为 true,禁用上传超时。 |
enableLookups | 关闭 DNS 反向查询,DNS 反查很耗时间 |
6、禁用AJP连接器
AJP:协议是一个面向包的。web 服务器和 servlet 容器通过 TCP 链接进行交互,为了节省 SOCKET 创建的昂贵代价,WEB 服务器会尝试维护一个永久 TCP 链接到Servlet 容器,并在多个请求和响应周期过程会重用链接。
Web 客户端访问 tomcat 服务器上 jsp 组件的 2 种方式:
由于 Tomcat 服务器相对于 Nginx 服务器在处理静态资源上效率较低。因此我们的网站服务器一般是 Nginx+Tomcat,Nginx 负责处理静态资源,因此 AJP 协议我们在使用 Nginx+Tomcat 架构时可以关闭它来进行效率的优化。
# server.xml 文件,将 AJP 服务禁用掉即可。
vim /usr/local/tomcat/conf/server.xml
<!-- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> -->
# 重启 tomcat
systemctl restart tomcat
# 测试:里面的 AJP 不见了
http://192.168.1.11:8080/manager/status
7、禁用8005端口
SHUTDOWN 端口是写在 server 参数里面的,一般在安全设置时候建议把端口修 改为其他端口,SHUTDOWN 修改为其他复杂的字符串。 实际上这个端口是可以直接屏蔽不监听的。设置时候将其 port 值修改为-1 就可以。
vim /usr/local/tomcat/conf/server.xml
<Server port="8005" shutdown="SHUTDOWN"> 修改为: <Server port="-1" shutdown="SHUTDOWN">
systemctl restart tomcat
netstat -antup | grep java
8、JVM参数优化
JVM 的内存空间分为 3 大部分: 堆内存、方法区、栈内存
堆内存可以划分为新生代(Young)和老年代(Tenured),新生代中还可以再次划分为 Eden区、From Survivor 区和 To Survivor 区。
栈内存
Java 栈是与每一个线程关联的,JVM 在创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文。栈空间随着线程的终止而释放。
堆内存
Java 中堆是由所有的线程共享的一块内存区域,堆用来保存各种 JAVA 对象,比如数组,线程对象等。对于大多数应用来说,Java 堆(Java Heap)是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存
Young(年轻代) | Tenured(老年代) |
对象在被创建时,内存首先是在年轻代进行分配(注意,大对象可以直接在老年代分配)。 当年轻代需要回收时会触发 Minor GC(也称作 Young GC,Garbage Collection,垃圾收集,垃圾回收)。 Young 区被划分为三部分,Eden 区和两个大小相同的 Survivor 区,其中 Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用, 在 Young 区间变满的时候,minor GC ([ˈmaɪnər])就会将存活的对象移到空闲的 Survivor 区间中,根据 JVM 的策略,在经过几次垃圾收集后,仍然存活于 Survivor的对象将被移动到 Tenured 区间。 | 老年代用于存放在年轻代中经多次垃圾回收仍然存活的对象,可以理解为比较老一点的对象,例如缓存对象,一般如果系统中用了 application 级别的缓存,缓存中的对象往往会被转移到这一区间。当一些对象在 Young 复制转移一定的次数以后,对象就会被转移到 Tenured 区。当老年代满了的时候就需要对老年代进行垃圾回收,老年代的垃圾回收称作 Major GC(也称作 Full GC)。 |
方法区
方法区存放了要加载的类的信息(如类名,修饰符)、类中的静态变量、final 定义的常量、类中的 field、方法信息,当开发人员调用类对象中的 getName、isInterface 等方法来获取信息时,这些数据都来源于方法区。方法区是全局共享的,在一定条件下它也会被 GC。当方法区使用的内存超过它允许的大小时,就会抛出 OutOfMemory:PermGen Space 异常。
在 JDK8 之前的 Hotspot 虚拟机中,这块区域对应的是 Permanent Generation(永久代),一般的,方法区上执行的垃圾收集是很少的,因此方法区又被称为持久代的原因之一。
随着 JDK8 的到来,JVM 不再有永久代(PermGen)。但类的元数据信息(metadata)还在,只不过不再是存储在连续的堆空间上,而是移动到叫做“Meta space”的本地内存(Native memory)。
对 jvm 参数的优化我们主要是对堆内存的优化,下图中的 Perm 代表的是永久代,但是注意永久代并不属于堆内存中的一部分。
堆内存大小
对于堆区大小,可以通过参数-Xms 和-Xmx 来控制,-Xms 为 JVM 启动时向系统申请的 heap 内存,默认为物理内存的 1/64,但小于 1GB;-Xmx 为 JVM 可申请的最大Heap 内存,默认为物理内存的 1/4,默认当剩余堆空间小于 40%时,JVM 会增大 Heap 到-Xmx 大小,可通过-XX:MinHeapFreeRatio 参数来控制这个比例;当空余堆内存大于 70%时,JVM 会减小 Heap 大小到-Xms 指定大小,可通过-XX:MaxHeapFreeRatio来指定这个比例。对于系统而言,为了避免在运行期间频繁的调整 Heap 大小,我 们通常将-Xms 和-Xmx 设置成一样。
年轻代
年轻代由 Eden Space 和两块相同大小的 Survivor Space(又称 S0 和 S1)构成,可通过-Xmn 参数来调整新生代大小,也可通过-XX:SurvivorRatio 来调整 Eden Space 和 Survivor Space 大小。
默认的,Eden : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
老年代
老年代所占用的内存大小为-Xmx 对应的值减去-Xmn 对应的值。年轻代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 – XX:NewRatio 来指定 )
JVM 方法区(永久代)
JVM 方法区的相关参数,最小值:--XX:PermSize;最大值 --XX:MaxPermSize。
# 在 catalina.sh 一大段注释的下面,正文配置的上面加入
vim /usr/local/tomcat/bin/catalina.sh
export JAVA_OPTS="-server -Xms128M -Xmx256M -Xss512k -XX:+AggressiveOpts -
XX:+UseBiasedLocking -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=15 -
XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -
XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -
XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -
Djava.awt.headless=true"
参数 | 说明 |
-server | 表示这是应用于服务器的配置 |
-Xms | 表示 java 虚拟机堆区内存初始内存分配的大小,虚拟机在启动时向系统申请的内存的大小。 |
-Xmx | 表示 java 虚拟机堆区内存可被分配的最大上限,通常会将 -Xms 与 -Xmx 两个参数的配置相同的值,初始堆内存与最大堆内存大小设为一致,这样虚拟机一 次性的分配内存,而不至于在初始堆大小不够用又向系统分配内存。 而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,应用可能会导致 java.lang.OutOfMemory 错误,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的 80%。 |
Xmn | 设置年轻代大小 |
-XX:newSize | 表示年轻代初始内存的大小,应该小于 -Xms 的值 |
-XX:MaxnewSize | 表示年轻代可被分配的内存的最大上限;当然这个值应该小于 - Xmx 的值; |
-Xmn | 至于这个参数则是对 -XX:newSize、-XX:MaxnewSize 两个参数的同时配置, 也 就 是 说 如 果 通 过 -Xmn 来 配 置 年 轻 代 的 内 存 大 小 , 那 么 -XX:newSize = -XX:MaxnewSize = -Xmn,虽然会很方便,但需要注意的是这个参数是在 JDK1.4 版本以后才使用的。 |
-Xss | 是指设定每个线程的栈大小。这个就要依据你的程序,看一个线程大约需要占用多少内存,可能会有多少线程同时运行等。 |
-XX:PermSize | 表示非堆区初始内存分配大小,其缩写为permanent size(持久化内存) |
-XX:MaxPermSize | 表示对非堆区分配的内存的最大上限。XX:MaxPermSize 设置过小会导致 java.lang.OutOfMemoryError: PermGen space 就是内存益出。这里面非常要注意的一点是:在配置之前一定要慎重的考虑一下自身软件所需要 的非堆区内存大小,因为此处内存是不会被 java 垃圾回收机制进行处理的地方。 并且更加要注意的是 最大堆内存与最大非堆内存的和绝对不能够超出操作系统的可用内存。 |
-XX:NewRatio | 设置年轻代(包括 Eden 和两个 Survivor 区)与老年代的比值(除去永久代)。设置为 2,则年轻代与终身代所占比值为 1:2,年轻代占整个堆栈的1/3。 |
-XX:SurvivorRatio=4 | 设置堆内存年轻代中 Eden 区与 Survivor 区大小的比值 。设置为 4,则两个 Survivor 区(JVM 堆内存年轻代中默认有 2 个 Survivor 区)与一个 Eden 区的比值为 2:4,一个 Survivor 区占 整个年轻代的 1/6。 |
-XX:MaxTenuringThreshold | 设置垃圾最大年龄,默认为:15。如果设置为 0 的话, 则年轻代对象不经过 Survivor 区,直接进入年老代。 对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活时间。 增加对象在年轻代即被回收的概率。 需要注意的是,设置了 -XX:MaxTenuringThreshold,并不代表着,对象一定在年轻代存活 15 次才被晋升进入老年代,它只是一个最大值,事实上,存在一个动态计算机制,计算每次晋入老年代的阈值,取阈值和 MaxTenuringThreshold 中较小的一 个为准。 |
-XX:+UseParNewGC | 设置年轻代为并发收集。 |
-XX:+UseConcMarkSweepGC | 设置年老代为并发收集。 |
-XX:ParallelGCThreads=20 | 配置并行收集器的线程数,即:同时多少个线程一起进行 垃圾回收。此值最好配置与处理器数目相等。 |
# 查看
systemctl restart tomcat
http://192.168.1.11:8080/manager/status