Tomcat作为Web服务器,它的处理性能直接关系到用户体验。
一、Tomcat固定的目录结构
二、Tomcat的配置
2.1 tomcat端口号的配置
tomcat端口号的缺省值为8080,如果修改端口号,可以用以下方法:
找到tomcat目录下的\conf\server.xml文件中下面的代码段,修改其中的port即可。
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
server.xml是Tomcat算是最基本的配置文件,WEB容器的基本配置都在这里,包括端口、虚拟机、线程池等配置信息。
2.2 如何加大tomcat内存
如果tomcat内存不够用,则会造成内存溢出的问题。这时通常会抛出java.lang.OutOfMemoryError: PermGen space异常,是指内存的永久保存区域OutOfMemoryError,解决办法就是加大内存。找到tomcat目录下的/bin/catalina.sh,在文件的前面,增加如下设置:
JAVA_OPTS='-Xms[初始化内存大小] -Xmx[可以使用的最大内存]'
【参数说明】
-Xms 是指设定程序启动时占用内存大小。一般来讲,大点,程序会启动的 快一点,但是也可能会导致机器暂时间变慢。
-Xmx 是指设定程序运行期间最大可占用的内存大小。如果程序运行需要占 用更多的内存,超出了这个设置值,就会抛出OutOfMemory 异常。
2.3 如何加大tomcat线程连接数
在tomcat配置文件server.xml中的配置中,和连接数相关的参数有:
<Connector port="9027"
protocol="HTTP/1.1"
maxHttpHeaderSize="8192"
maxThreads="1000" -- 客户请求最大线程数
minSpareThreads="100" --Tomcat初始化时创建的 socket 线程数
maxSpareThreads="1000" --Tomcat连接器的最大空闲 socket 线程数
minProcessors="100" -- 服务器创建时的最小处理线程数
maxProcessors="1000" --最大连接线程数,即:并发处理的最大请求数,默认值为75
enableLookups="false" --是否反查域名
URIEncoding="utf-8" --URL统一编码
acceptCount="1000" --允许的最大连接数,应大于等于maxProcessors,默认值为100
redirectPort="8443" --在需要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口
disableUploadTimeout="true"
connectionTimeout=30000 --网络连接超时,单位:毫秒。设置为0表示永不超时
/>
其中和最大连接数相关的参数为maxProcessors和acceptCount。如果要加大并发连接数,应同时加大这两个参数。web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。
2.4 设置tomcat缓存
在tomcat配置文件server.xml中的配置中,和缓存相关的参数有:
<Connector port="9027"
protocol="HTTP/1.1"
-- 是否启用压缩 on为启用(文本数据压缩)
compression="on"
-- 启用压缩的输出内容大小,这里面默认为2KB,只有当响应报文大小大于这个值的时候才会对报文进行压缩
compressionMinSize="2048"
-- 配置想压缩的数据类型
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
-- 定义建立客户连接超时的时间
connectionTimeout="20000"
disableUploadTimeout="true"/>
数据被压缩了,响应速度自然减少了。但是有时候你会发现compressionMinSize这个配置并不起作用,貌似只要客户端加了Accept-Encoding: gzip, deflate,响应就是压缩的,无论响应报文体有多大,这是怎么回事呢?
Tomcat判断是否压缩的的依据是根据你响应里的Content-length来的,如果没有Content-length或者Content-length大于compressionMinSize就进行压缩。如果你的报文小于compressionMinSize,却被压缩了,那就是你的响应报文中并不含有Content-length。
如果你的响应无论什么大小都被压缩了,那很有可能就是你在代码中使用了OutputStream.flush(),或者PrintWriter.flush()。使用flush()的意思是响应开始往外写了,Content-length已经来不及写了,tomcat会默认使用Transfer-Encoding: chunked形式帮你返回消息,响应肯定是要被压缩的。
所以如果你的设置compressionMinSize的没有生效,很有可能是你的设置的compressionMinSize超出了tomcat的缓冲区大小,响应默认是chunked的形式返回的,这种情况是肯定压缩的。
三、Tomcat是如何加载的
Windows下JAVA用到的环境变量主要有3个,JAVA_HOME、CLASSPATH、PATH。
- JAVA_HOME 指向的是JDK的安装路径。
JAVA_HOME=C:\jdk1.5.0_06 - PATH 环境变量原来Windows里面就有,你只需修改一下,使他指向JDK的bin目录,这样你在控制台下面编译、执行程序时就不需要再键入一大串路径了。
PATH=%JAVA_HOME%\bin;%PATH%
其中%PATH%是引用以前你设置的PATH环境变量 - CLASSPATH 环境变量配置的是java类库的位置以及执行字节码文件时查找文件的路径。
CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
要注意的是最前面的".;",这个是告诉JDK,搜索CLASS时先查找当前目录的CLASS文件。
Tomcat用到的环境变量主要有CATALINA_HOME、CATALINA_BASE
- CATALINA_HOME:Tomcat的安装目录
- CATALINA_BASE:Tomcat的工作目录
JVM类加载
当JVM运行过程中,用户需要加载某些类时,会按照下面的步骤(父类委托机制):
1 用户自己的类加载器,把加载请求传给父加载器,父加载器再传给其父加载器,一直到加载器树的顶层。
2 最顶层的类加载器首先针对其特定的位置加载,如果加载不到就转交给子类。
3 如果一直到底层的类加载都没有加载到,那么就会抛出异常。
因此,按照这个过程可以想到,假设两个包名和类名完全相同的class文件不再同一个jar包,如果一个class文件已经被加载java虚拟机里了,那么后面的相同的class文件就不会被加载了。
Tomcat类加载
当tomcat启动时,会创建几种类加载器:
1 Bootstrap 引导类加载器
是其他类加载器的父类,它用于加载Java核心库,并且是唯一一个用本地代码编写的类加载器(位于jre/lib/ext下)
2 System 系统类加载器
加载tomcat启动的类,比如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于CATALINA_HOME/bin下。
**3 Common 通用类加载器 **
加载tomcat使用以及应用通用的一些类,位于CATALINA_HOME/lib下,比如servlet-api.jar
4 webapp 应用类加载器
每个应用在部署后,都会创建一个唯一的类加载器。该类加载器会加载位于 WEB-INF/lib下的jar文件中的class 和 WEB-INF/classes下的class文件。WEB-INF为项目的根目录,基本地包含一下文件夹:
- classes文件夹:放置已编译的java类
- config文件夹:放置项目使用的配置文件
- lib文件夹:放置jar库文件(许多class文件的集合)
- web.xml
当应用需要到某个类时,则会按照下面的顺序进行类加载:
1 使用bootstrap引导类加载器加载
2 使用system系统类加载器加载
3 使用应用类加载器在WEB-INF/classes中加载
4 使用应用类加载器在WEB-INF/lib中加载
5 使用common类加载器在CATALINA_HOME/lib中加载
从上面的加载机制我们就可以知道,src下面的java文件优先于jar包中的class,这是因为src文件夹中的java类以及webContent中的JSP都会在tomcat启动时编译成class文件放置在WEB-INF/classes中;而项目外部引用的jar包则是放置在WEB-INF/lib下。
同时要注意,在同一个文件夹下,jar包是按顺序从上到下依次加载,如果已经找到,则不会再后续jar包中进行查找。另外如果在 CATALINA_HOME/lib 以及 WEB-INF/lib 中放置了不同版本的jar包,此时就会导致某些情况下报加载不到类的错误。
四、Tomcat的部署方式
(1)静态部署
- 将web项目文件件拷贝到webapps 目录中
- 在server.xml中指定
在tomcat中的conf目录中,在server.xml中的,<host/>节点中添加:
<Context path="/hello" docBase="D:\ workspace\hello\WebRoot" debug="0" privileged="true"/>
【参数说明】
path:虚拟路径
docBase :应用程序的物理路径
debug :设定debug level, 0表示提供最少的信息,9表示提供最多的信息
privileged:设置为true的时候,才允许Tomcat的Web应用使用容器内的Servlet
- 创建一个Context文件
在conf目录中,新建 Catalina\localhost目录,在该目录中新建一个xml文件,名字不可以随意取,要和path后的那个名字一致,按照下边这个path的配置,xml的名字应该就应该是hello(hello.xml),该xml文件的内容为:
<Context path="/hello" docBase="D:\ workspace\hello\WebRoot" debug="0" privileged="true"/>
这个方法有个优点,可以定义别名。服务器端运行的项目名称为path,外部访问的URL则使用XML的文件名。这个方法很方便的隐藏了项目的名称,对一些项目名称被固定不能更换,但外部访问时又想换个路径,非常有效。
(2)动态部署
可以用tomcat在线后台管理器,一般tomcat都打开了,直接上传war就可以。
五、Tomcat的运行模式
Tomcat如何处理多个用户同时请求的问题呢?有以下三种方式:
(1)BIO:默认的模式,性能非常低下,没有经过任何优化处理和支持。bio模式同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。
(2)NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
想运行在该模式下,直接修改server.xml里的Connector节点,修改protocol为nio:
<Connector port=”80″ protocol=”org.apache.coyote.http11.Http11NioProtocol”
connectionTimeout=”20000″
URIEncoding=”UTF-8″
useBodyEncodingForURI=”true”
enableLookups=”false”
redirectPort=”8443″ />
(3)APR:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。安装起来最困难,但是从操作系统级别来解决异步的IO问题,大幅度的提高性能。
BIO、NIO、AIO适用场景分析:
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
六、Tomcat的优化
(1)内存优化
默认情况下Tomcat的相关内存配置较低,这对于一些大型项目显然是不够用的,这些项目运行就已经耗费了大部分内存空间,何况大规模访问的情况。可以用-Xmx -Xms -XX:MaxPermSize等参数对内存不同功能块进行划分。内存分配不足,就导致虚拟机一直处于full GC,从而导致处理能力严重下降。
(2)线程优化
默认情况下 Tomcat只支持200线程访问,超过这个数量的连接将被等待甚至超时放弃,所以我们需要提高这方面的处理能力,优化Connector(连接器)。
(3)IO优化
在server.xml中
<Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
URIEncoding="UTF-8"
useBodyEncodingForURI="true"
enableLookups="false"
redirectPort="8443" />
实现对Tomcat的IO切换.
(4)利用缓存和压缩
硬盘主要问题就是读写性能,当大量文件进行读写时,磁盘极容易成为性能瓶颈。最好的办法还是利用下面提到的缓存。
对于静态页面最好是能够缓存起来,这样就不必每次从磁盘上读。这里我们采用了Nginx作为缓存服务器,将图片、css、js文件都进行了缓存,有效的减少了后端tomcat的访问。
另外,为了能加快网络传输速度,开启gzip压缩也是必不可少的。但考虑到tomcat已经需要处理很多东西了,所以把这个压缩的工作就交给前端的Nginx来完成。
(5)采用集群
单个服务器性能总是有限的,最好的办法自然是实现横向扩展,那么组建tomcat集群是有效提升性能的手段。