读书笔记《apache-tomcat-7-essentials》性能调整
想象你正在度假,享受旅行,凌晨 2 点,你的电话响了。你迅速拿起手机,发现老板打电话说 ABC 公司的网络系统出现故障,你必须上网解决问题。如果你不想面对这些情况,请仔细阅读本章。
在本章中,我们将介绍性能调优的主要主题,包括以下内容:
内存相关问题
JVM参数优化
操作系统级别优化以提高性能
性能调优对于在不停机的情况下运行 Web 应用程序起着至关重要的作用。此外,它还有助于在运行应用程序时提高 Tomcat 的性能。 Tomcat 服务器的调整可能因应用程序而异。由于每个应用程序都有自己的要求,因此为每个应用程序调整 Tomcat 7 是一项非常棘手的任务。在本主题中,我们将讨论 Tomcat 的各种组件的调优,以及它们如何在服务器性能方面发挥作用。在开始配置更改之前,让我们快速讨论一下为什么需要调整 Tomcat。
人们总是问,为什么我们需要对 Tomcat 7 进行性能调优,默认情况下,Tomcat 7 包是为生产运行定制的。
这个问题的答案是非常主观的;每个 Web 应用程序都有自己的要求。一些应用程序需要高内存,而其他应用程序需要较少的内存但高 GC 暂停。它因应用程序而异,管理员必须根据应用程序的要求调整 Tomcat。
JVM 调优的性能调优仍然不完整。还有许多其他因素在应用程序的性能中也起着关键作用,例如数据库配置、操作系统级别设置和应用程序使用的硬件。下图展示了在 Tomcat 7 上进行的不同类型的性能调优:
应用程序代码:如果应用程序代码开发不当,可能会导致性能问题。例如,如果来自应用程序代码的数据库连接没有正确关闭,那么它将在数据库中创建一个陈旧的连接,从而导致应用程序运行缓慢。< /一>
数据库调优: 数据库可能会导致 Tomcat 中托管的应用程序的性能出现重大问题。例如,如果数据库响应缓慢,那么它会向应用程序查询发送延迟响应,从而导致应用程序在 Tomcat 7 中运行缓慢。
JVM 调优: 每个应用程序都有自己的内存需求。如果应用程序(比如 abc)有非常大的内存需求并且您分配的内存较少,那么您可能会遇到 OOM(内存不足)问题,从而导致性能问题。
中间件服务:中间件服务可能会导致应用程序与外部接口的连接问题。例如,如今,移动应用程序使用 Web 服务连接到服务器并获取应用程序数据。这样,我们只是将 Web 服务暴露给 Internet,而不是整个应用服务器。
基础设施和操作系统:基础设施问题也可能导致性能下降。例如,如果网络出现 Internet 连接问题或网络丢包,则会导致性能下降。
我们将在第 7 章,Tomcat 中的疑难解答中讨论解决上述问题的不同方法。
性能调优对于在不停机的情况下运行 Web 应用程序起着至关重要的作用。此外,它还有助于在运行应用程序时提高 Tomcat 的性能。 Tomcat 服务器的调整可能因应用程序而异。由于每个应用程序都有自己的要求,因此为每个应用程序调整 Tomcat 7 是一项非常棘手的任务。在本主题中,我们将讨论 Tomcat 的各种组件的调优,以及它们如何在服务器性能方面发挥作用。在开始配置更改之前,让我们快速讨论一下为什么需要调整 Tomcat。
人们总是问,为什么我们需要对 Tomcat 7 进行性能调优,默认情况下,Tomcat 7 包是为生产运行定制的。
这个问题的答案是非常主观的;每个 Web 应用程序都有自己的要求。一些应用程序需要高内存,而其他应用程序需要较少的内存但高 GC 暂停。它因应用程序而异,管理员必须根据应用程序的要求调整 Tomcat。
JVM 调优的性能调优仍然不完整。还有许多其他因素在应用程序的性能中也起着关键作用,例如数据库配置、操作系统级别设置和应用程序使用的硬件。下图展示了在 Tomcat 7 上进行的不同类型的性能调优:
应用程序代码:如果应用程序代码开发不当,可能会导致性能问题。例如,如果来自应用程序代码的数据库连接没有正确关闭,那么它将在数据库中创建一个陈旧的连接,从而导致应用程序运行缓慢。< /一>
数据库调优: 数据库可能会导致 Tomcat 中托管的应用程序的性能出现重大问题。例如,如果数据库响应缓慢,那么它会向应用程序查询发送延迟响应,从而导致应用程序在 Tomcat 7 中运行缓慢。
JVM 调优: 每个应用程序都有自己的内存需求。如果应用程序(比如 abc)有非常大的内存需求并且您分配的内存较少,那么您可能会遇到 OOM(内存不足)问题,从而导致性能问题。
中间件服务:中间件服务可能会导致应用程序与外部接口的连接问题。例如,如今,移动应用程序使用 Web 服务连接到服务器并获取应用程序数据。这样,我们只是将 Web 服务暴露给 Internet,而不是整个应用服务器。
基础设施和操作系统:基础设施问题也可能导致性能下降。例如,如果网络出现 Internet 连接问题或网络丢包,则会导致性能下降。
我们将在第 7 章,Tomcat 中的疑难解答中讨论解决上述问题的不同方法。
性能调优从应用程序部署阶段开始的那一天开始。有人可能会问,既然应用只是在开发阶段,为什么我们现在需要做性能调优呢?
在应用程序开发时,我们处于定义应用程序架构、应用程序在现实中将如何执行以及应用程序需要多少资源的状态。没有预定义的性能调整步骤。但是,所有管理员都应遵循某些经验法则来进行性能调整。
正确的日志记录和监控: 应该为 Tomcat 启用正确的日志记录,这将有助于跟踪生产中即将发生的潜在问题系统。我们将在第 6 章,在 Tomcat 7 中登录中讨论启用 logger 的不同方法跨度>。始终建议定期监视系统。
Note
市场上有许多可用的监控工具,可以让您了解应用程序的真实情况。大型企业使用这些工具来分析应用程序。例如,JON(Jboss on Network)、CA Wily、Nagios、Panorama 等等。
我们已经讨论了调优 Tomcat 7 的方法。现在,是时候对 Tomcat 7 进行实时性能调优了。
在 Tomcat 7 中,您可以进行许多配置来提高服务器性能、线程调优、端口定制和 JVM 调优。让我们快速讨论一下 Tomcat 7 的主要组件,它们对性能提升很重要。
连接器可以定义为接受请求并返回响应的交叉点。 Tomcat 7 中使用了三种类型的连接器,如下图所示。这些连接器根据不同的应用要求使用。让我们讨论每个连接器的可用性:
Java HTTP 连接器基于 HTTP 协议,该协议仅支持 HTTP/1.1 协议。它使 Tomcat 服务器充当独立的 Web 服务器,并启用托管 JSP/servlet 的功能。
Note
有关 HTTP 连接器的更多信息,请访问 http: //tomcat.apache.org/tomcat-7.0-doc/config/http.html。
Java AJP 连接器基于 AJP(Apache JServ 协议 ) 并通过 AJP 与 Web 服务器通信。当您不想将 Java servlet 容器暴露给 Internet(使用不同的前端服务器)时,此连接器主要使用,并且在 SSL 未在 Tomcat 7 中终止的情况下也非常有用。实现 AJP 的一些示例分别是 mod_jk、mod _proxy 等等。
Note
有关 AJP 连接器的更多信息,请访问 URL http ://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html。
Apache Portable Runtime (APR) 在可扩展性、不同的 Web 服务器需要性能和更好的协作。它提供了额外的功能,例如 Open SSL、共享内存、Unix 套接字等。它还使 Java 成为一种 Web 服务器技术,而不是被后端技术所吸引。我们将在第 4 章,Tomcat 与 Apache 的集成中讨论 Tomcat 连接器的实时实现网络服务器.
Note
有关 APR 连接器的更多详细信息,请访问链接 http: //tomcat.apache.org/tomcat-7.0-doc/apr.html。
线程调优对 Tomcat 的性能起着重要作用。在大多数情况下,我们已经看到一个特定的应用程序在其他行业中运行良好,但是当我们实现相同的应用程序时,它的性能会下降(应用程序性能问题)。原因是我们可能对线程进行了不正确的调整,这可能导致服务器性能下降。让我们讨论一下线程调优线程池的不同组成部分。
线程池可以定义为 Web 服务器接受 Tomcat 7 中处理的连接或请求数的容量。我们可以配置两种类型的线程池;共享池和专用池。这些配置需要在 server.xml
中完成,放在 TOMCAT_HOME/conf/server.xml
中。让我们讨论这些方法及其配置。
它可以定义为一个线程池,在许多连接器之间共享。例如,如果您有四个连接器配置,那么您可以为所有连接器共享同一个线程池。以下是配置共享线程池的过程:
1. 编辑
server.xml
,在services部分添加共享线程池的定义。以下突出显示的代码显示了线程池:<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
2. 定义共享线程池后,调用
server.xml
的services部分的Connector定义中线程设置的引用,如下代码所示:<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
我们以 server.xml
中给出的默认示例来解释共享池。在实践中,您可以根据业务需求定义共享池。
它可以定义为一个专用于一个连接器定义的线程池。例如,如果应用程序期望高负载,那么使用专用线程池对于连接器来说更好,以便顺利管理 Tomcat 实例。以下是配置专用线程池的过程。突出显示的代码定义了专用连接池的值:
1. 编辑
server.xml
,在Connector部分定义专用线程池的配置。以下突出显示的代码显示了线程池:<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" />
让我们快速比较一下共享线程池和专用线程池,看看它们可以在哪里使用。下表显示了 Tomcat 7 的两种线程池方法之间的比较:
特征 |
共享线程池 |
专用线程池 |
---|---|---|
用户数 |
少 |
高的 |
环境 |
发展 |
生产 |
表现 |
低的 |
好的 |
maxThreads 可以定义为服务器可以接受的最大请求数。默认情况下,Tomcat 7 带有 maxThreads=150
。在生产环境中,我们必须根据服务器性能调整 maxThreads
。
让我们执行一次实时 maxThreads
调优。假设我们有一个应用程序的 maxThreads=300
。现在,是时候确定线程调整对服务器的影响了。如果 maxThreads
配置不正确,可能会降低服务器性能。如何查找对服务器的影响?
解决方案是检查服务器的 CPU 利用率。如果 CPU 利用率很高,则降低线程的值。这意味着线程已经降低了服务器性能,但如果 CPU 利用率正常,则增加线程的值以容纳更多并发用户。
在我们开始 JVM 调优之前,我们应该注意到市场上有各种 JVM 供应商。根据应用需求,选择厂商的JDK。
Tomcat 7 的堆大小为 256 MB。今天的应用程序需要大内存才能运行。为了运行应用程序,我们必须调整 Tomcat 7 的 JVM 参数。让我们快速讨论一下 Tomcat 的默认 JVM 配置。以下步骤描述了找出 Tomcat 进程 ID (PID< /span>) 和内存值,如下图所示:
在 Linux 的终端上运行以下命令:
ps -ef |grep java
这将返回系统中运行的所有 Java 进程,以及所有信息,例如 PID、Tomcat 运行的位置等:
root 4306 1 0 14:09 pts/1 00:00:04 /opt/jdk1.6.0_24/bin/java -Djava.util.logging.config.file=/opt/apache- tomcat-7.0.12/conf/logging.properties - Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager - Djava.endorsed.dirs=/opt/apache-tomcat-7.0.12/endorsed -classpath /opt/apache-tomcat-7.0.12/bin/bootstrap.jar:/opt/apache-tomcat- 7.0.12/bin/tomcat-juli.jar -Dcatalina.base=/opt/apache-tomcat-7.0.12 - Dcatalina.home=/opt/apache-tomcat-7.0.12 -Djava.io.tmpdir=/opt/apache- tomcat-7.0.12/temp org.apache.catalina.startup.Bootstrap start
在前面的输出中, 4306
是 Tomcat 进程的 PID。现在,我们知道了 Tomcat 的进程 ID,让我们使用命令 jmap
找出分配给 Tomcat 实例的内存。
要在 Windows 上检查 Tomcat 的进程 ID,您必须运行以下命令:
tasklist |find "tomcat"
以下屏幕截图显示了上一条命令的输出,其中 2112
是 Tomcat 进程的进程 ID:
JMAP 打印共享 Java 虚拟内存的完整图片。它是管理员检查共享内存状态的一个很好的工具。该命令为执行命令提供了不同的选项。下表描述了 JMAP 的有用选项:
选项 |
描述 |
---|---|
|
以 |
|
打印关于等待完成的对象的信息 |
|
打印堆摘要 |
|
打印堆的直方图 |
|
打印 Java 堆永久生成的类加载器统计信息 |
Note
有关 JMAP 命令的更多信息,请访问 http://docs.oracle.com/javase/6/docs/technotes/tools/share/jmap.html。
使用 jmap 命令的语法是 ./jmap -heap <process id>
其中<process id>
是我们要检查内存的 Java 进程。
在前面的语法中, -heap
选项会打印堆摘要、使用的 GC 算法、堆配置和生成的堆使用情况。
在我们当前的场景中,PID 是 4306:
[root@localhost bin]# ./jmap -heap 4306
上一条命令的输出如下:
Attaching to process ID 4306, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 19.1-b02
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 268435456 (256.0MB) NewSize = 1048576 (1.0MB) MaxNewSize = 4294901760 (4095.9375MB) OldSize = 4194304 (4.0MB) NewRatio = 2 SurvivorRatio = 8 PermSize = 12582912 (12.0MB) MaxPermSize = 67108864 (64.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 5111808 (4.875MB)
used = 3883008 (3.703125MB)
free = 1228800 (1.171875MB)
75.96153846153847% used
Eden Space:
capacity = 4587520 (4.375MB)
used = 3708360 (3.5365676879882812MB)
free = 879160 (0.8384323120117188MB)
80.83583286830357% used
From Space:
capacity = 524288 (0.5MB)
used = 174648 (0.16655731201171875MB)
free = 349640 (0.33344268798828125MB)
33.31146240234375% used
To Space:
capacity = 524288 (0.5MB)
used = 0 (0.0MB)
free = 524288 (0.5MB)
0.0% used
tenured generation:
capacity = 11206656 (10.6875MB)
used = 3280712 (3.1287307739257812MB)
free = 7925944 (7.558769226074219MB)
29.274673908077485% used
Perm Generation:
capacity = 12582912 (12.0MB)
used = 6639016 (6.331459045410156MB)
free = 5943896 (5.668540954589844MB)
52.762158711751304% used
应用程序的堆配置(突出显示的代码描述了堆配置)
每个 JVM 组件的堆利用率
用于垃圾收集的算法
如果你看前面的堆分配,那么你可以清楚地区分分配给 Tomcat 7 的内存。它是 256 MB,有 12 MB 的 Perm Generation。
为了增加 Tomcat 7 的堆大小,我们需要在 catalina.sh
中添加 JAVA_OPTS
参数,可以在 TOMCAT_HOME/bin
.
让我们举一个将最大堆大小增加到 512 MB 而不是 256 MB 的示例。同时设置 Perm Gen = 256 MB
。
JAVA_OPTS="-Xms128m -Xmx512m -XX:MaxPermSize=256m"
[root@localhost bin]# jmap -heap 21091
上一条命令的输出如下:
Attaching to process ID 21091, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 19.1-b02
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 536870912 (512.0MB)
NewSize = 1048576 (1.0MB)
MaxNewSize = 4294901760 (4095.9375MB)
OldSize = 4194304 (4.0MB)
NewRatio = 2
SurvivorRatio = 8
PermSize = 12582912 (12.0MB)
MaxPermSize = 268435456 (256.0MB)
上一个代码片段中突出显示的代码行反映了 JVM 参数中循环后更改的值。
我们也可以在 catalina.sh
中定义 JRE_HOME, JAVA_OPTS, JAVA_ENDORSED_DIRS, JPDA_TRANSPORT, JPDA_ADDRESS, JPDA_SUSPEND, JPDA_OPTS, LOGGING_CONFIG, LOGGING_MANAGER
参数.
做了加内存的实时实现之后,我们就明白了 JVM 调优的必要性,以及在 Tomcat 7 中堆值是如何增减的。另外一个大家常说的名词是 垃圾收集 (GC)。没有垃圾回收,JVM 调优是不完整的。
Garbage 意味着浪费,但让我们找出这个 waste 术语如何适用于 JVM。
垃圾只不过是一个驻留在 JVM 内存中的对象,当前没有被任何程序使用。
垃圾收集器是一种周期性运行的算法,收集内存中活动和非活动对象的状态,并删除非活动对象以释放内存。
当调用 GC 算法时,它会收集内存中存在的所有非活动对象,从而清理内存。可以解释为手动内存管理的反面:
它从内存中删除所有非活动对象,所有活动线程将保留在内存中。上图展示了 GC 前后内存的状态。实时环境中使用的 GC 收集器主要有以下三种:
下表描述了串行收集器的功能:
特征 |
串行收集器 |
---|---|
过程 |
GC使用单线程 |
GC 暂停 |
高 |
穿线 |
单线程 |
应用 |
小型应用程序(数据小于 100 MB) |
优势 |
有单线程通信 |
特征 |
并行收集器 |
---|---|
过程 |
并行线程执行次要 GC |
GC 暂停 |
小于串行 |
穿线 |
多线程 |
应用 |
中型 |
优势 |
在需要峰值性能的应用中使用 |
特征 |
并发收集器 |
---|---|
过程 |
GC 是同时进行的 |
GC 暂停 |
短暂的停顿 |
穿线 |
多线程 |
应用 |
中型 |
优势 |
在需要响应时在应用程序中使用 |
Java HotSpot VM 选项分为两类;标准和非标准选项。
非标准选项在 JVM 中使用 -X
或 -XX
选项定义。这些选项分为三类:
下表描述了 JVM 非常常用的选项:
选项 |
范围 |
描述 |
---|---|---|
行为选择 |
|
在full GC之前进行年轻代GC |
行为选择 |
|
使用并行垃圾收集进行清理 |
性能选项 |
|
新生代的最大大小(以字节为单位) |
性能选项 |
|
永久代的大小(超过 |
性能选项 |
|
Tomcat启动的最小堆内存 |
性能选项 |
|
分配给实例的最大内存 |
性能选项 |
|
堆的堆栈大小 |
调试选项 |
|
打印在 JIT 编译器中花费的时间 |
调试选项 |
|
如果发生错误,将错误数据保存到此文件 |
调试选项 |
|
堆转储的目录或文件名的路径 |
调试选项 |
|
抛出 |
选项 |
范围 |
描述 |
调试选项 |
|
在致命错误上运行用户定义的命令 |
调试选项 |
|
首次抛出 OutOfMemoryError 时运行用户定义的命令 |
调试选项 |
|
在 Ctrl-Break 上打印类实例的直方图 |
Note
有关 JVM 非标准选项的更多信息,请访问链接 http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html。
每个操作系统都有其运行 Tomcat 7 的先决条件,并且必须根据应用程序的要求对系统进行调整,但每个操作系统之间存在一些相似之处。让我们讨论用于针对每个操作系统优化 Tomcat 7 的通用模块。操作系统对于提高性能起着至关重要的作用。根据硬件,应用程序的性能会增加或减少。对应用程序非常有用的一些要点是:
[root@localhost bin]# cat /proc/meminfo MemTotal: 1571836 kB MemFree: 886116 kB Buffers: 74712 kB Cached: 430088 kB SwapCached: 0 kB Active: 308608 kB Inactive: 331944 kB HighTotal: 671680 kB HighFree: 97708 kB LowTotal: 900156 kB LowFree: 788408 kB SwapTotal: 2040212 kB SwapFree: 2040212 kB Dirty: 36 kB Writeback: 0 kB AnonPages: 135764 kB Mapped: 54828 kB Slab: 33840 kB PageTables: 3228 kB NFS_Unstable: 0 kB Bounce: 0 kB CommitLimit: 2826128 kB Committed_AS: 496456 kB VmallocTotal: 114680 kB VmallocUsed: 4928 kB VmallocChunk: 109668 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 Hugepagesize: 4096 kB