1、目的
通过优化tomcat提高网站的并发能力。当我们今天我们将这个优化讲完之前 优化完成后看能达到什么层次.
2、服务器资源
服务器所能提供CPU、内存、硬盘的性能对处理能力有决定性影响。 硬件我们不说了 这个方面是钱越多越好是吧。
3、优化配置
【3.1】获取tomcat管理员账号
在conf/ tomcat-users.xml下添加用户:
<role rolename="manager"/>
<role rolename="manager-gui"/>
<role rolename="admin"/>
<role rolename="admin-gui"/>
<user username="tomcat" password="tomcat" roles="admin-gui,admin,manager-gui,manager"/>
tomcat版本7以前包括7均默认为bio(同步阻塞)模型,7版本以后默认为nio(同步非阻塞)模型
ajp-服务器之间的通信协议,socket层 然后组装一定的数据给对方 数据形式也可能json xml 文本 只是各个厂家约定的,例如我们在一个物理主机跑三个TOMCAT服务器的时候是吧,这个负载均衡 通过权数和算法
【3.2】tomcat的3中运行模式
1、bio
默认的模式,性能非常低下,没有经过任何优化处理和支持. 一个线程处理一个请求。缺点:并发量高时,线程数较多,浪费资源。
Tomcat7或以下,在Linux系统中默认使用这种方式。
2、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)更好的并发运行性能。
利用Java的异步IO处理,可以通过少量的线程处理大量的请求。Tomcat8在Linux系统中默认使用这种方式。
Tomcat7必须修改Connector配置来启动:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000" redirectPort="8443"/>
3、apr
安装起来最困难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能.即Apache Portable Runtime,从操作系统层面
解决io阻塞问题。Tomcat7或Tomcat8在Win7或以上的系统中启动默认使用这种方式。Linux如果安装了apr和native,
Tomcat直接启动就支持apr。具体安装办法百度
4、在那里看我们的tomcat以何种工作模式启动的啊?
Tomcat启动的时候,可以通过log看到Connector使用的是哪一种运行模式:
Starting ProtocolHandler ["http-bio-8080"]
Starting ProtocolHandler ["http-nio-8080"]
Starting ProtocolHandler ["http-apr-8080"]
【3.2.1】启动nio模式
修改server.xml里的Connector节点,修改protocolorg.apache.coyote.http11.Http11NioProtocol
这里有个问题啊? 为什么它不直接改成NIO的形式呢?
这个方式是利用了jdk 1.4及后续版本提供的一种新的I/O操作方式(即java.nio包及其子包)为桥梁实现的,不改成这种优化方式就是为了兼容1.4之前的JAVA项目也能跑起来。
【3.3】执行器(线程池)
默认的tomcat没有启用线程池,
在tomcat中每一个用户请求都是一个线程,所以可以使用线程池提高性能。这里前台其实有一个调度线程,然后调度线程会放入线程池内,然后到到一定的时候线程池的任务变成工作线程啊。
【3.3.1】
【3.3.2】参数说明
threadPriority (优先级)
(int)线程的线程优先级执行程序,默认是5(NORM_PRIORITY常数)
daemon(守护进程)
(布尔)是否应该守护程序线程,线程默认是true
namePrefix(名称前缀)
(String) The name prefix for each thread created by the executor. The thread name
for an individual thread will be namePrefix+threadNumber
maxThreads(最大线程数)
(int) The max number of active threads in this pool, default is 200
minSpareThreads(最小活跃线程数)
(int) The minimum number of threads always kept alive, default is 25
maxIdleTime(空闲线程等待时间)
(int) 一个空闲的线程shutsdown之前的毫秒数,除非活动线程的数量不等于minSpareThreads。默认值为60000(1分钟)
maxQueueSize(最大的等待队里数,超过则请求拒绝)
(int) 可运行的最大数量可以排队等待执行的任务之前,我们拒绝他们。默认值是Integer.MAX_VALUE
prestartminSpareThreads(是否在启动时就生成minSpareThreads个线程)
minSpareThreads是否应该开始在开始执行程序,默认是false
threadRenewalDelay(重建线程的时间间隔)
(long)重建线程池内的线程时,为了避免线程同时重建,每隔threadRenewalDelay
(单位: ms )重建一个线程。默认值为1000 ,设置为负则不重建
【3.3.3】最佳配置
【3.3.4】3.4.连接器(Connector)优化
我们知道TOMCAT_HOME/conf/server.xml可以配置端口,虚拟路径等等 Tomcat相关主要配置。
1.Connector 优化
Connector是连接器,负责接收客户的请求,以及向客户端回送响应的消息。所以 Connector的优化是重要部分。默认情况下
Tomcat只支持200(优化手段三可以根据服务配置 可以改变这个值)线程访问,超过结果:超过这个数量的连接将被等待甚至超时放
弃,所以我们需要提高这方面的处理能力。
修改这部分配置需要修改TOMCAT_HOME/conf/server.xml,打开server.xml找到Connector 标签项,默认配置如下:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
port代表服务接口;
protocol代表协议类型;
connectionTimeout代表连接超时时间,单位为毫秒;
redirectPort代表安全通信(https)转发端口,一般配置成443。
可以看到除了这几个基本配置外并无特殊功能,所以我们需要对 Connector 进行扩展。 其中Connector 支持参数属性可以参考Tomcat
官方网站(https://tomcat.apache.org/tomcat-8.0-doc/config/http.html)
修改为如下配置:
<Connector port="8080"
protocol="HTTP/1.1"
maxThreads="1000"
minSpareThreads="100"
acceptCount="1000"
maxConnections="1000"
connectionTimeout="20000"
maxHttpHeaderSize="8192"
tcpNoDelay="true"
compression="on"
compressionMinSize="2048"
disableUploadTimeout="true"
redirectPort="8443"
enableLookups="false"
URIEncoding="UTF-8" />
Connector是Tomcat接收请求的入口,每个Connector有自己专属的监听端口8088端口 接受http请求
Connector有两种:HTTP Connector和AJP Connector
一个Service节点代表了一个或者多个Connector和一个Engine,而Connector和Engine是在server.xml中两个重 要的配置项,Connector的主要功能是接受、响应用户请求。常用的Connector有HTTP/1.1 Connector和AJP Connector,HTTP/1.1 Connector主要用于处理用户的HTTP请求,需要注意的是虽然它名叫HTTP/1.1 Connector,但是是完全兼容HTTP/1.0协议的。AJP Connector主要使用AJP协议和Web Connector通信,通常用于集群中。
HTTP/1.1 Connector的实例监听在用户配置的端口上,当应用服务器启动时,HTTP/1.1 Connector负责创建若干线程,用于处理用户请求,创建的线程数目取决于用户配置的minThreads值,默认为5,当有更多的用户请求到来 时,HTTP/1.1 Connector将会创建更多的线程用于处理请求,创建线程的最大值由maxThreads定义,默认值为20,当所有的线程都在忙于处理用户请求时, 新到来的请求将会放入HTTP/1.1 Connector创建的Socket队列中,队列的长度由acceptCount属性定义,当等待队列也被占用满了,新来的用户请求将会收到connection refused错误。
所有的Connector提供的配置项(不完全版scheme, isSecure, xpoweredBy, useIPVHosts ):
allowTrace
如果需要服务器能够处理用户的HAED/TRACE请求,这个值应该设置为true,默认值是false;
emptySessionPath
如果设置为true,所有session,cookie的path将会被设置为/,这种设置通常是在portlet中比较有用,默认值是false;
enableLookups
如果需要在调用request.getRemoteHost()方法时获取到客户端的机器名,则需要配置为true,如果配置为false,将会跳过DNS查询直接返回客户端机器的IP地址,通常为了提高性能,将此值设置为false,默认值是true;
maxPostSize
POST方法能够提交的数据的最大大小,如果没有声明或者设置为小于等于0,则表示POST提交的数据大小是不限制的,默认值是2Megabytes.
protocol
设置处理请求的协议,默认是HTTP/1.1,即org.apache.coyote.http11.Http11Protocol,此外还 支持的协议有:org.apache.coyote.http11.Http11NioProtocol(通过NIO处理用户请求,可以提高系统性能), org.apache.coyote.http11.HttpAprProtocol。
proxyName/proxyPort
如果Web服务器使用了代理服务器,配置此参数意味着在调用request.getServerName的时候将会获取代理服务器的名称,getServerPort()将会返回proxyPort。
redirectPort
如果Connector的配置是支持非SSL的请求,当一个SSL请求到来时,服务器会自动的将请求重定位到redirectPort。
URIEncoding
URI字节转化成String的时候的编码方式,默认为ISO-8859-1,如果页面需要支持中文,一般可以将其设置为UTF-8或者GBK,GB2312。
useBodyEncodingForURI
如果设置为true,则会根据页面的编码决定URI的编码方式,默认是false。
Http/1.1 Connector提供的配置项:
acceptCount
等待队列的长度,默认值是100。
address
如果Tomcat所在的主机有多个IP,这个值声明了用于监听HTTP请求的IP地址。
bufferSize
Connector创建的输入流的大小,默认值是2048 bytes,提高这个值可以提升性能,增加内存消耗。
compressableMimeType
使用HTTP压缩的MIME类型,使用逗号分割,默认值是 text/html,text/xml,text/plain。
compression
为了节省带宽,可以将这个值设置为on,从而启用HTTP/1.1 GZIP压缩。off关闭压缩,forces强制使用压缩,默认值是off。
connectionTimeout
Connector接受一个连接后等待的时间(milliseconds),默认值是60000。
executor
在Service节点下,Connector节点前可以配置一个Executor节点用于管理线程,这个属性的值是配置的Executor的名称,如果应用了此属性且executor存在,那么任何其他的关于thread的配置将会被忽略。
keepAliveTimeout
在Connector关闭连接前,Connector为另外一个请求Keep Alive所等待的微妙数,默认值和 connectionTimeout 一样。
maxHttpHeaderSize
HTTP请求、响应头信息的最大大小,默认是8192bytes。
maxKeepAliveRequests
HTTP/1.0 Keep Alive 和HTTP/1.1 Keep Alive / Pipeline的最大请求数目,如果设置为1,将会禁用掉Keep Alive和Pipeline,如果设置为小于0的数,Keep Alive的最大请求数将没有限制。默认为100。
maxThreads
用于处理用户请求的最大线程数,默认值是20。
noCompressionUserAgents:
设置不使用HTTP GZIP压缩的客户端,使用逗号分隔,在某些浏览器不支持压缩的时候可以使用此属性。
port
Connector监听的端口。
restrictedUserAgents
设置不使用Keep Alive的客户端代理名称,使用逗号分割,默认值是空字符串。
server
覆盖HTTP响应的serve头信息,如果不设置的话,默认值是 Apache-Coyote/1.1。一般情况下不需要关注此属性。
socketBuffer
Socket输出流缓冲区的大小,默认是9000bytes,如果设置为小于0的值,则表示不使用此缓冲区。
tcpNoDelay
默认值是true,设置为true可以提高系统性能。
threadPriority
请求处理线程的优先级,默认的优先级是NORMAL。
【3.4.4】最佳实践
【4】4.禁用AJP连接器
AJP(Apache JServer Protocol)
AJPv13协议是面向包的。WEB服务器和Servlet容器通过TCP连接来交互;为了节省SOCKET创建的昂贵代价,WEB服务器会尝试维护一个永久TCP连接到servlet容器,并且在多个请求和响应周期过程会重用连接。
我们一般是使用Nginx+tomcat的架构,所以用不着AJP协议,所以把AJP连接器禁用。
在管理界面中看不到ajp了:
【5】jvm参数优化
因为Tomcat运行在JAVA虚拟机之上啊
适当调整Tomcat的运行JVM参数可以提升整体性能。
5.1.JVM内存模型
5.1.1.Java栈
Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文。栈空间随着线程的终止而释放。
5.1.2.Java堆
Java中堆是由所有的线程共享的一块内存区域,堆用来保存各种JAVA对象,比如数组,线程对象等。
5.1.3.Java堆的分区:JVM堆一般又可以分为以下三部分:
◆ Young 年轻区(代)
Young区被划分为三部分,Eden区和两个大小严格相同的Survivor区,其中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到Tenured区间。
◆ Tenured 年老区
Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别(spring容器放在application)的缓存,缓存中的对象往往会被转移到这一区间。
◆ Perm 永久区
Perm代主要保存class,method,filed对象,这个区域不会被gc回收。这部份的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到java.lang.OutOfMemoryError : PermGen space 的错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的class没有被卸载掉,这样就造成了大量的class对象保存在了perm中,这种情况下,一般重新启动应用服务器可以解决问题。
tomcat热部署:是指在你修改项目BUG的时候对JSP或JAVA类进行了修改在不重启WEB服务器前提下能让修改生效。但是对配置文件的修改除外!
◆Virtual区:虚拟区
最大内存和初始内存的差值,就是Virtual区。
【5.1.4】设置区大小
JVM提供了相应的参数来对内存大小进行配置。正如上面描述,JVM中堆被分为了3个大的区间,同时JVM也提供了一些选项对Young,Tenured的大小进行控制。
如何设置年轻代大小?哪些应用系统需要调整年轻代 老年代 永久代?
这个需要根据应用场景的特点设置啊,如果说我们需要经常创建对象啊 而且对象使用完后马上会被回收的,这种场景年轻代可以适当调大。
比如说:对外提供一个查询数据的接口,返回数据 查询出来的对象转换成json对象 然后这个接口频繁访问 的我们可以适当调大一点。
老年代: 静态变量什么的
◆ Total Heap
-Xms :指定了JVM初始启动以后初始化内存
-Xmx:指定JVM堆得最大内存,在JVM启动以后,会分配-Xmx参数指定大小的内存给JVM,但是不一定全部使用,JVM会根据-Xms参数来调节真正用于JVM的内存
-Xmx -Xms之差就是三个Virtual空间的大小
◆ Young Generation
-XX:NewRatio=8意味着tenured 和 young的比值8:1,这样eden+2*survivor=1/9
堆内存
-XX:SurvivorRatio=32意味着eden和一个survivor的比值是32:1,这样一个Survivor就占Young区的1/34.
-Xmn 参数设置了年轻代的大小
◆ Perm Generation
-XX:PermSize=16M -XX:MaxPermSize=64M
Thread Stack
-XX:Xss=128K
【5.2】常用参数
修改文件:bin/catalina.sh
JAVA_OPTS="-Dfile.encoding=UTF-8 -server –Xms512m -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=512m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:NewRatio=2 -XX:MaxTenuringThreshold=50 -XX:+DisableExplicitGC"
参数说明:
1、file.encoding 默认文件编码
2、-Xmx1024m 设置JVM最大可用内存为1024MB
3、-Xms1024m 设置JVM最小内存为1024m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
4、-XX:NewSize 设置年轻代大小
5、XX:MaxNewSize 设置最大的年轻代大小
6、-XX:PermSize 设置永久代大小
7、-XX:MaxPermSize 设置最大永久代大小
8、-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与终身代的比值(除去永久代)。设置为4,则年轻代与终身代所占比值为1:4,年轻代占整个堆栈的1/5
9、-XX:MaxTenuringThreshold=0:设置垃圾最大年龄,默认为:15。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
10、-XX:+DisableExplicitGC这个将会忽略手动调用GC的代码使得 System.gc()的调用就会变成一个空调用,完全不会触发任何GC
【5.3.1】
修改bin/catalina.bat文件设置参数(第一行 93行)
set JAVA_OPTS=-Dfile.encoding=UTF-8 -server -Xms1024m -Xmx2048m -XX:NewSize=512m -XX:MaxNewSize=1024m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC
【5.3.2】
修改bin/catalina.sh文件参数(第一行)
JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms1024m -Xmx2048m -XX:NewSize=512m -XX:MaxNewSize=1024m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC"
【6】禁用DNS
当web应用程序向要记录客户端的信息时,它也会记录客户端的IP地址或者通过域名服务器查找机器名转换为IP地址。DNS查询需要占用网络,并且包括可能从很多很远的服务器或者不起作用的服务器上去获取对应的IP的过程,这样会消耗一定的时间。为了消除DNS查询对性能的影响我们可以关闭DNS查询
修改server.xml文件中的enableLookups参数值改为false:enableLookups="false"