四. Tomcat性能优化
1. 嵌入式 Tomcat
1. 为什么要嵌入式
为什么需要嵌入式启动,我们由之前一般的 Tomcat 启动可知,Tomcat 组件非常多,启动流程步骤比较多,但是往往我们一般就只需要简单快速的部署一个 Web 项目,所以简单实用的嵌入式 Tomcat 就诞生而来
部署复杂度
如果按照传统部署,我们需要1. 下载 Tomcat,同时2. 需要配置服务器,同时还需要3. 修改端口,4. 同时也要避免应用系统的 jar 包与服务器中存在的 lib 包的冲突,所有的这些都会增加部署的复杂度,5. 并且这种配置大部分还是一次性的,不可重用。如果你遇到大规模的服务器集群环境(部署 N 多个应用)时, 会增加我们的运维成本,如果按照嵌入式启动,**(_)**这种方式几乎是一键式的,可以把以上问题轻松的解决。
架构约束(启动过重)
Tomcat 启动的时候默认不单单只启动了 HTTP 协议,还有 AJP 协议等等,如果我们就只想简简单单的启动一个 HTTP 服务,同时不想启动那些多组件, 可以使用嵌入式 Tomcat,避免在部署启动时的架构约束。
微服务架构(趋势)
现在微服务已经是主流的架构,其中微服务中每项服务都拥有自己的进程并利用轻量化机制实现通讯。这些服务都是围绕业务功能建立,可以自动化部署或独立部署。将微服务架构与 Tomcat 技术相结合,可以轻松将系统部署到云服务器上。当前 SpringBoot 支持的嵌入式服务器组件就是Tomcat(当然还有Jetty, Undertow).
2. 嵌入式启动实战
启动 Servlet
加入嵌入式tomcat依赖(jsp不是必须的)
<!--嵌入式Tomcat -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.34</version>
</dependency>
<!--嵌入式Tomcat的JSP 支持-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>8.5.34</version>
</dependency>
嵌入式启动的方式启动一个SpringMVC的项目
public static void main(String[] args) throws Exception{
Tomcat tomcat = new Tomcat();
tomcat.addWebapp("/mvc","D:\\work_tomcat\\spring_mvc");
tomcat.getConnector().setPort(80);
tomcat.init();
tomcat.start();
tomcat.getServer().await();
}
嵌入式启动的方式启动一个Servlet
public static void main(String[] args) throws Exception {
//自定义的一个Servlet(专门处理http请求)
HttpServlet httpServlet = new HttpServlet() {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
res.getWriter().write("hello world!");
}
};
//引入嵌入式Tomcat
Tomcat tomcat = new Tomcat();
//部署应用的context
Context context = tomcat.addContext("/demo",null);
//相当于往应用中添加Servlet
tomcat.addServlet(context,"hello",httpServlet);
//相当于添加了servletMapping 映射信息
context.addServletMappingDecoded("/hello","hello");
//启动Tomcat ---生命周期
tomcat.init();
tomcat.start();
tomcat.getServer().await();//用于阻塞Tomcat,等待请求过来
//http://localhost:8080/demo/hello
}
http://localhost:8080/demo/hello
这里很简单, 对于请求没有做任何handler的映射处理, 直接返回固定数据
通过实战可知,我们添加了一个 HttpServlet 提供对外服务,这样我们的 Tomcat 就可以简单的提供一个 Servlet 服务,而不提供页面访问(JSP、HTML 等),可以非常灵活的控制 Servlet 的加载以及请求链接的分配,而不受限制于 Context 与应用的一一对应关系。
嵌入式启动应用
public static void main(String[] args) throws Exception{
Tomcat tomcat = new Tomcat();
tomcat.addWebapp("/mvc","D:\\work_tomcat\\spring_mvc"); // 指定上下文和引用的位置
tomcat.getConnector().setPort(80);
tomcat.init();
tomcat.start();
tomcat.getServer().await();
}
核心就是 指定上下文和引用的位置
嵌入式常用的配置
setConnector: 用于设置链接器,包括协议、I/0、端口、压缩、加密等等。
setHost: 用于设置 Host
setBaseDir: 用于设置临时文件的目录,这些目录用于存放 JSP 生成的源代码及 Class 文件
3. SpringBoot 中使用嵌入式 Tomcat
略
主要就是通过Spring的refresh流程中的onRefresh方法去启动start方法, 然后new Tomcat()对象, 原理一模一
2. Tomcat 性能优化
1. server.xml 优化
只要下载了 Tomcat,并且启动 了它,那么 Tomcat 就会提供最官方,最准确的官方参数说明文档。
下载 Tomcat 后不要删掉默认的程序包.

一般 Tomcat 启动后,不改动端口的话,默认是 8080,我们输入 localhost:8080
访问下。
1. Connector 连接器
Common Attributes
可以使用工具翻一下
IO 模型优化策略
连接器模式改为 NIO 模式,NIO 模式最大化压榨了 CPU,把时间片更好利用起来,NIO 适合大量长连接。
最大线程优化策略(压榨CPU)
-2, 表示未使用该值
maxThreads 属性设置为简单 200.这对于单个核心计算机来说很好,但是可以根据生产计算机上的处理器数量线性增加。在具有四个处理器的计算机 上,将此值设置为 800 到 1000 之间的任何值都不会导致问题。如果配置的数量最终远远超过所需的线程数,则当服务器负载较低时,线程池将自然缩减 此数字。
压缩 gzip 连接器传输(CPU时间换网络时间)
客户端和服务器之间的任何主要是文本的通信,无论是 HTML,XML 还是简单的 Unicode,都可以使用简单的标准 GZIP 算法定期压缩高达 90%。这可 以对减少网络流量产生巨大影响,允许响应更快地发送回客户端,同时允许更多网络带宽可用于其他网络繁重的应用程序。
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"
executor="tomcatThreadPool" URIEncoding="utf-8"
<!--开启压缩-->
compression="on" compressionMinSize="50" noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
/>
2. 配置线程池 Executor
<Executor
name="tomcatThreadPool" <!--线程名称-->
namePrefix="catalina-exec-"
maxThreads="150" <!--最大处理连接数线程-->
minSpareThreads="4" <!--保留最少线程数-->
/>
<Connector
executor="tomcatThreadPool"
port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
minProcessors="5"<!-- 同时处理请求的最小数 -->
maxProcessors="75"<!-- 同时处理请求的最大数 -->
acceptCount="1000" <!-- 接受最大并发数量 ,超过这个数量就会返回连接被拒绝 -->
/>
server.xml中已经注释掉的
Executor 的属性
去除 valve 访问 tomcat 记录
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
关闭自动重载,热部署方式
关闭自动重载,默认是 true(不同版本中有差异),自动加载增加运行开销并且很容易内存溢出
2. web.xml 优化
1. 去掉不必要的 servlet
如当前应用是 REST 应用(微服务):
-
去掉不必要的资源:JspServlet
-
seesion 也可以移除
2. JspServlet 优化
- checkInterval - 如果“development”属性为 false 且“checkInterval”大于 0,则使用后台编译。“checkInterval”是查看 JSP 页面(包括其附属文件)
是否需要重新编译的两次检查时间间隔(单位:秒)。缺省值为 0 秒。
-
classdebuginfo - 类文件在编译时是否显示调试(debugging)信息? true 或 false,缺省为 true。
-
classpath - 编译 servlet 时要使用的类路径,当 ServletContext 属性 org.apache.jasper.Constants.SERVLET_CLASSPATH 未设置的情况下,
该参数才有效。在 Tomcat 中使用到 Jasper 时,该属性总被设置。缺省情况下,该路径基于你当前的 web 应用动态生成。
- compiler – Ant 将要使用的 JSP 页面编译器,请查阅 Ant 文档获取更多信息。如果该参数未设置,那么默认的 Eclipse JDT Java 编译器将被用
来代替 Ant。没有缺省值。
-
compilerSourceVM - 编译源文件时采用哪一个 JDK 版本?(缺省为 JDK 1.4)
-
compilerTargetVM - 运行类文件时采用哪一个 JDK 版本?(缺省为 JDK 1.4)
-
development - 是否让 Jasper 用于开发模式?如果是,检查 JSPs 修改的频率,将通过设置 modificationTestInterval 参数来完成。
true 或 false,
缺省为 true。
-
displaySourceFragment - 异常信息中是否包含出错的源代码片段?true 或 false,缺省为 true。
-
dumpSmap - JSR45 调试的 SMAP 信息是否转存到文件?true 或 false,缺省为 false。当 suppressSmap 为 true 时,该参数为 false。
-
enablePooling - 确定是否共享标签处理器,true 或 false,缺省为 true。
-
engineOptionsClass - 允许指定的类来配置 Jasper。如果没有指定,则使用默认的 Servlet 内置参数(EmbeddedServletOptions)。
-
errorOnUseBeanInvalidClassAttribute - 在一个 useBean action 中,当类属性的值不是一个合法的 bean class 时,Jasper 是否抛出异常?true
或 false,缺省为 true。
-
fork - 是否让 Ant 派生出 JSP 页面多个编译,它们将运行在一个独立于 Tomcat 的 JVM 上。true 或者 false, 缺省为 true.
-
enStringAsCharArray - 是否把字符串转换为字符数组?在某些情况下会改善性能。缺省为 false.
-
eClassId - 当使用标签时,发送给 Internet Explorer 的 class-id 的值。缺省为:8AD9C840-044E-11D1-B3E9-00805F499D93。
-
javaEncoding - 生成 java 源文件时采用的 Java 文件编码。缺省为 UTF-8。
-
keepgenerated - 是否保存每个页面生成的 java 源代码,而不删除。true 或 false,缺省为 true。
-
mappedfile - 是否对每个输入行都用一条 print 语句来生成静态内容,以方便调试。true 或 false,缺省为 true。
SP 每一次访问都会被检查。仅仅适用于开发模式(参数 development 为 true)。缺省为 4 秒。从 JSP 每次开始访问开始计时,N 秒以后检查,变化就编译,每次访问都刷新开始时间,默认 4 秒
-
scratchdir - 当编译 JSP 页面时使用的 scratch 目录。缺省为当前 WEB 应用的工作目录。
-
suppressSmap - 是否禁止 JSR45 调试时生成 SMAP 信息?true 或 false,缺省为 false。
-
trimSpaces - 是否去掉模板文本中行为和指令之间的空格。缺省为 false。
-
xpoweredBy - 确定生成的 Servlet 是否加上 X-Powered-By 响应头?true 或 false,缺省为 false。