Tomcat 安全配置与性能优化

一.Tomcat内存优化

1.JAVA_OPTS参数说明

Tomcat内存优化主要是对 tomcat 启动参数优化,咱们能够在 tomcat 的启动脚本 catalina.sh 中设置 JAVA_OPTS参数。javascript

服务器参数配置php

配置完成后可重启Tomcat ,经过如下命令进行查看配置是否生效:css

1.  首先查看Tomcat 进程号:html

 ps -ef|grep java

  

咱们能够看到Tomcat 进程号是 12222 。前端

 

1.  查看是否配置生效:java

 sudo jmap -heap 12222

  

咱们能够看到MaxHeapSize 等参数已经生效。linux

 

优化 server.xml

Tomcat的主配置文件,该文件中包含不少主要元素,好比Service、Connector、Host等,这些元素都会建立软件"对象"、排序及进程管道中设置的这些元素嵌套方,使咱们能够执行过滤、分组等工做。nginx

若是要对该文件作优化,咱们须要先了解该文件的结构!web

server.xml的结构图:shell

该文件描述了如何启动Tomcat Server

复制代码
<Server>
  <Listener />
  <GlobaNamingResources>
  </GlobaNamingResources
  <Service>
    <Connector />
    <Engine>
      <Logger />
      <Realm />
         <host>
           <Logger />
           <Context />
         </host>
    </Engine>
  </Service>
</Server>
复制代码

针对该文件,咱们须要优化的点有以下:

一、 maxThreads 链接数限制

maxThreads 是 Tomcat 所能接受最大链接数。通常设置不要超过8000以上,若是你的网站访问量很是大可能使用运行多个Tomcat实例的方法, 即,在一个服务器上启动多个tomcat而后作负载均衡处理。

这里还须要注意的一点是,tomcat 和 php 不一样。php能够按照cpu和内存的状况去配置链接数,上万很正常。而 java 还须要注意 jvm 的参数配置。若是不注意就会由于jvm参数太小而崩溃。

二、多虚拟主机

强烈建议不要使用 Tomcat 的虚拟主机,推荐每一个站点使用一个实例。即,能够启动多个 Tomcat,而不是启动一个 Tomcat 里面包含多个虚拟主机。由于 Tomcat是多线程,共享内存,任何一个虚拟主机中的应用崩溃,都会影响到全部应用程序。虽然采用多实例的方式会产生过多的开销,但至少保障了应用程序的隔离和安全。

三、压错传输

tomcat做为一个应用服务器,也是支持 gzip 压缩功能的。咱们能够在 server.xml 配置文件中的 Connector 节点中配置以下参数,来实现对指定资源类型进行压缩。

compression="on"             # 打开压缩功能 
compressionMinSize="50"      # 启用压缩的输出内容大小,默认为2KB 
noCompressionUserAgents="gozilla, traviata"      # 对于如下的浏览器,不启用压缩 
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" # 哪些资源类型须要压缩

提示:

Tomcat 的压缩是在客户端请求服务器对应资源后,从服务器端将资源文件压缩,再输出到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程 HTML、CSS、Javascript和Text,它能够节省40% 左右的流量。更为重要的是,它能够对动态生成的,包括CGI、PHP、JSP、ASP、Servlet,SHTML等输出的网页也能进行压缩,压缩效率也很高。可是, 压缩会增长 Tomcat 的负担,所以最好采用Nginx + Tomcat 或者 Apache + Tomcat 方式,将压缩的任务交由 Nginx/Apache 去作。

一旦启用了这个压缩功能后,咱们怎么来测试压缩是否有效呢?首先Tomcat是根据浏览器请求头中的accept-encoding来判断浏览器是否支持 压缩功能,若是这个值包含有gzip,就代表浏览器支持gzip压缩内容的浏览,因此咱们能够用httpclient来写一个这样的简单测试程序

复制代码
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
 
public class HttpTester {
 
public static void main(String[] args) throws Exception{
HttpClient http = new HttpClient();
GetMethod get = new GetMethod("http://www.dlog.cn/js/prototype.js");
try{
get.addRequestHeader("accept-encoding", "gzip,deflate");
get.addRequestHeader("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Alexa Toolbar; Maxthon 2.0)");
int er = http.executeMethod(get);
if(er==200){
System.out.println(get.getResponseContentLength());
String html = get.getResponseBodyAsString();
System.out.println(html);
System.out.println(html.getBytes().length);
}
}finally{
get.releaseConnection();
}
}
 
}
复制代码

执行这个测试程序,看看它所输出的是什么内容,若是输出的是一些乱码,以及打印内容的长度远小于实际的长度,那么恭喜你,你的配置生效了,你会发现你网站的浏览速度比之前快多了。

四、管理AJP端口

AJP是为 Tomcat 与 HTTP 服务器之间通讯而定制的协议,能提供较高的通讯速度和效率。若是tomcat前端放的是apache的时候,会使用到AJP这个链接器。因为咱们公司前端是由nginx作的反向代理,所以不使用此链接器,所以须要注销掉该链接器。

<!--
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
-->

五、更改关闭 Tomcat 实例的指令

server.xml中定义了能够直接关闭 Tomcat 实例的管理端口。咱们经过 telnet 链接上该端口以后,输入 SHUTDOWN (此为默认关闭指令)便可关闭 Tomcat 实例(注意,此时虽然实例关闭了,可是进程仍是存在的)。因为默认关闭 Tomcat 的端口和指令都很简单。默认端口为8005,指令为SHUTDOWN 。所以咱们须要将关闭指令修改复杂一点。

固然,在新版的 Tomcat 中该端口仅监听在127.0.0.1上,所以你们也没必要担忧。除非黑客登录到tomcat本机去执行关闭操做。

修改实例:

<Server port="8005" shutdow n="9SDKJ29jksjf23sjf0LSDF92JKS9DKkjsd">

六、更改 Tomcat 的服务监听端口

通常公司的 Tomcat 都是放在内网的,所以咱们针对 Tomcat 服务的监听地址都是内网地址。

修改实例:

<Connector port="8080" address="172.16.100.1" />

七、关闭war自动部署

默认 Tomcat 是开启了对war包的热部署的。为了防止被植入木马等恶意程序,所以咱们要关闭自动部署。

修改实例:

<Host name="localhost"  appBase=""
            unpackWARs="false" autoDeploy="false">

 

二.Tomcat并发优化

1.调整链接器connector的并发处理能力

在Tomcat 配置文件 server.xml 中的 <Connector ... /> 配置中

1.参数说明

maxThreads  客户请求最大线程数

minSpareThreads    Tomcat初始化时建立的 socket 线程数

maxSpareThreads   Tomcat链接器的最大空闲 socket 线程数

minProcessors:最小空闲链接线程数,用于提升系统处理性能,默认值为 10

maxProcessors:最大链接线程数,即:并发处理的最大请求数,默认值为 75

acceptCount:容许的最大链接数,应大于等于 maxProcessors ,默认值为 100

enableLookups:是否反查域名,取值为: true 或 false 。为了提升处理能力,应设置为 false

redirectPort        在须要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口

acceptAccount       监听端口队列最大数,满了以后客户请求会被拒绝(不能小于maxSpareThreads  )

connectionTimeout:网络链接超时,单位:毫秒。设置为 0 表示永不超时,这样设置有隐患的。一般可设置为30000 毫秒。

URIEncoding    URL统一编码

其中和最大链接数相关的参数为maxProcessors 和 acceptCount 。若是要加大并发链接数,应同时加大这两个参数。

web server容许的最大链接数还受制于操做系统的内核参数设置,一般 Windows 是 2000 个左右, Linux 是1000 个左右。

2.Tomcat中的配置示例

复制代码
<Connector port="9027"   
                protocol="HTTP/1.1"  
                maxHttpHeaderSize="8192"  
                maxThreads="1000"  
                minSpareThreads="100"  
                maxSpareThreads="1000"  
                minProcessors="100"  
                maxProcessors="1000"  
                enableLookups="false"  
                URIEncoding="utf-8"  
                acceptCount="1000"  
                redirectPort="8443"  
                disableUploadTimeout="true"/>
复制代码

3.Tomcat缓存优化

 

tomcat的maxThreads、acceptCount(最大线程数、最大排队数)

<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="800" acceptCount="1000"/> 
tomcate -->config --> server.xml
 
其中最后两个参数意义以下:

maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200

acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100

 

这两个值如何起做用,请看下面三种状况

状况1:接受一个请求,此时tomcat起动的线程数没有到达maxThreads,tomcat会起动一个线程来处理此请求。

状况2:接受一个请求,此时tomcat起动的线程数已经到达maxThreads,tomcat会把此请求放入等待队列,等待空闲线程。

状况3:接受一个请求,此时tomcat起动的线程数已经到达maxThreads,等待队列中的请求个数也达到了acceptCount,此时tomcat会直接拒绝这次请求,返回connection refused

maxThreads如何配置

通常的服务器操做都包括量方面:1计算(主要消耗cpu),2等待(io、数据库等)

第一种极端状况,若是咱们的操做是纯粹的计算,那么系统响应时间的主要限制就是cpu的运算能力,此时maxThreads应该尽可能设的小,下降同一时间内争抢cpu的线程个数,能够提升计算效率,提升系统的总体处理能力。

第二种极端状况,若是咱们的操做纯粹是IO或者数据库,那么响应时间的主要限制就变为等待外部资源,此时maxThreads应该尽可能设的大,这样才能提升同时处理请求的个数,从而提升系统总体的处理能力。此状况下由于tomcat同时处理的请求量会比较大,因此须要关注一下tomcat的虚拟机内存设置和linux的open file限制。

我在测试时遇到一个问题,maxThreads我设置的比较大好比3000,当服务的线程数大到必定程度时,通常是2000出头,单次请求的响应时间就会急剧的增长,

百思不得其解这是为何,四处寻求答案无果,最后我总结的缘由多是cpu在线程切换时消耗的时间随着线程数量的增长愈来愈大,

cpu把大多数时间都用来在这2000多个线程直接切换上了,固然cpu就没有时间来处理咱们的程序了。

之前一直简单的认为多线程=高效率。。其实多线程自己并不能提升cpu效率,线程过多反而会下降cpu效率。

当cpu核心数<线程数时,cpu就须要在多个线程直接来回切换,以保证每一个线程都会得到cpu时间,即一般咱们说的并发执行。

因此maxThreads的配置绝对不是越大越好。

现实应用中,咱们的操做都会包含以上两种类型(计算、等待),因此maxThreads的配置并无一个最优值,必定要根据具体状况来配置。

最好的作法是:在不断测试的基础上,不断调整、优化,才能获得最合理的配置。

acceptCount的配置,我通常是设置的跟maxThreads同样大,这个值应该是主要根据应用的访问峰值与平均值来权衡配置的。

若是设的较小,能够保证接受的请求较快相应,可是超出的请求可能就直接被拒绝

若是设的较大,可能就会出现大量的请求超时的状况,由于咱们系统的处理能力是必定的。

提示

不少作过php运维的朋友在这里会犯一个大错误,php优化服务器一般怎作法是安装cpu以及内存的状况配置链接数,链接数过万都很正常,但java不一样jvm配置要很是当心,稍有差错就会崩溃。

maxThreads 配置要结合 JVM -Xmx 参数调整,也就是要考虑内存开销。

 

在线上环境中咱们是采用了tomcat做为Web服务器,它的处理性能直接关系到用户体验,在平时的工做和学习中,概括出如下七种调优经验。

1. 服务器资源

    服务器所能提供CPU、内存、硬盘的性能对处理能力有决定性影响。
    (1) 对于高并发状况下会有大量的运算,那么CPU的速度会直接影响处处理速度。
    (2) 内存在大量数据处理的状况下,将会有较大的内存容量需求,能够用-Xmx -Xms -XX:MaxPermSize等参数对内存不一样功能块进行划分。咱们以前就遇到过内存分配不足,致使虚拟机一直处于full GC,从而致使处理能力严重降低。
    (3) 硬盘主要问题就是读写性能,当大量文件进行读写时,磁盘极容易成为性能瓶颈。最好的办法仍是利用下面提到的缓存。

2. 利用缓存和压缩

    对于静态页面最好是可以缓存起来,这样就没必要每次从磁盘上读。这里咱们采用了Nginx做为缓存服务器,将图片、css、js文件都进行了缓存,有效的减小了后端tomcat的访问。

    另外,为了能加快网络传输速度,开启gzip压缩也是必不可少的。但考虑到tomcat已经须要处理不少东西了,因此把这个压缩的工做就交给前端的Nginx来完成。能够参考以前写的《利用nginx加速web访问》。

    除了文本能够用gzip压缩,其实不少图片也能够用图像处理工具预先进行压缩,找到一个平衡点可让画质损失很小而文件能够减少不少。曾经我就见过一个图片从300多kb压缩到几十kb,本身几乎看不出来区别。

3. 采用集群

    单个服务器性能老是有限的,最好的办法天然是实现横向扩展,那么组建tomcat集群是有效提高性能的手段。咱们仍是采用了Nginx来做为请求分流的服务器,后端多个tomcat共享session来协同工做。能够参考以前写的《利用nginx+tomcat+memcached组建web服务器负载均衡》。

4. 优化tomcat参数

    这里以tomcat7的参数配置为例,须要修改conf/server.xml文件,主要是优化链接配置,关闭客户端dns查询。

复制代码
<Connector port="8080"              
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="500"
minSpareThreads="20"
acceptCount="100"
disableUploadTimeout="true"
enableLookups="false"
URIEncoding="UTF-8" />
复制代码

5. 改用APR库

    tomcat默认采用的BIO模型,在几百并发下性能会有很严重的降低。tomcat自带还有NIO的模型,另外也能够调用APR的库来实现操做系统级别控制。

<Connector port="80" protocol="HTTP/1.1"        connectionTimeout="20000"         redirectPort="8443" />

修改成:

<Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol "   connectionTimeout="20000"     redirectPort="8443" />

    NIO模型是内置的,调用很方便,只须要将上面配置文件中protocol修改为org.apache.coyote.http11.Http11NioProtocol,重启便可生效。上面配置我已经改过了,默认的是HTTP/1.1。

    APR则须要安装第三方库,在高并发下会让性能有明显提高。具体安装办法能够参考http://www.cnblogs.com/huangjingzhou/articles/2097241.html。安装完成后重启便可生效。如使用默认protocal就是apr,但最好把将protocol修改为org.apache.coyote.http11.Http11AprProtocol,会更加明确。

    在官方找到一个表格详细说明了这三种方式的区别:

    

 

Java Blocking Connector

Java Nio Blocking Connector

APR/native Connector

 

BIO

NIO

APR

Classname

AjpProtocol

AjpNioProtocol

AjpAprProtocol

Tomcat Version

3.x onwards

7.x onwards

5.5.x onwards

Support Polling

NO

YES

YES

Polling Size

N/A

maxConnections

maxConnections

Read Request Headers

Blocking

Sim Blocking

Blocking

Read Request Body

Blocking

Sim Blocking

Blocking

Write Response

Blocking

Sim Blocking

Blocking

Wait for next Request

Blocking

Non Blocking

Non Blocking

Max Connections

maxConnections

maxConnections

maxConnections

6. 优化网络

    Joel也明确提出了优化网卡驱动能够有效提高性能,这个对于集群环境工做的时候尤其重要。因为咱们采用了linux服务器,因此优化内核参数也是一个很是重要的工做。给一个参考的优化参数:

1. 修改/etc/sysctl.cnf文件,在最后追加以下内容:  
复制代码
net.core.netdev_max_backlog = 32768 
net.core.somaxconn = 32768 
net.core.wmem_default = 8388608 
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
 net.core.wmem_max = 16777216 
net.ipv4.ip_local_port_range = 1024 65000 net.ipv4.route.gc_timeout = 100 
net.ipv4.tcp_fin_timeout = 30 
net.ipv4.tcp_keepalive_time = 1200 
net.ipv4.tcp_timestamps = 0 
net.ipv4.tcp_synack_retries = 2 
net.ipv4.tcp_syn_retries = 2 
net.ipv4.tcp_tw_recycle = 1 
net.ipv4.tcp_tw_reuse = 1 
net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_max_syn_backlog = 65536
复制代码
2. 保存退出,执行sysctl -p生效

7. 让测试说话

    优化系统最忌讳的就是只调优不测试,有时不适当的优化反而会让性能更低。以上全部的优化方法都要在本地进行性能测试事后再不断调整参数,这样最终才能达到最佳的优化效果。

 

补充Bio、Nio、Apr模式的测试结果:

    对于这几种模式,我用ab命令模拟1000并发测试10000词,测试结果比较意外,为了确认结果,我每种方式反复测试了10屡次,而且在两个服务器上都测试了一遍。结果发现Bio和Nio性能差异很是微弱,难怪默认竟然仍是Bio。可是采用apr,链接创建的速度会有50%~100%的提高。直接调用操做系统层果真神速啊,这里强烈推荐apr方式!

 

Tomcat的四种基于HTTP协议的Connector性能比较

Tomcat从5.5版本开始,支持如下四种Connector的配置分别为:


<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol"                           connectionTimeout="20000" redirectPort="8443"/>
<Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000"
               redirectPort="8443"/> 
<Connector executor="tomcatThreadPool"
               port="8081" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
<Connector executor="tomcatThreadPool"
               port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol"
               connectionTimeout="20000"
               redirectPort="8443" />

咱们姑且把上面四种Connector按照顺序命名为 NIO, HTTP, POOL, NIOP

为了避免让其余因素影响测试结果,咱们只对一个很简单的jsp页面进行测试,这个页面仅仅是输出一个Hello World。假设地址是 http://tomcat1/test.jsp

咱们依次对四种Connector进行测试,测试的客户端在另一台机器上用ab命令来完成,测试命令为: ab -c 900 -n 2000 http://tomcat1/test.jsp ,最终的测试结果以下表所示(单位:平均每秒处理的请求数):

 

NIO HTTP POOL NIOP
281 65 208 365
666 66 110 398
692 65 66 263
256 63 94 459
440 67 145 363

由这五组数据不难看出,HTTP的性能是很稳定,可是也是最差的,而这种方式就是Tomcat的默认配置。NIO方式波动很大,但没有低于280 的,NIOP是在NIO的基础上加入线程池,多是程序处理更复杂了,所以性能不见得比NIO强;而POOL方式则波动很大,测试期间和HTTP方式同样,不时有停滞。

因为linux的内核默认限制了最大打开文件数目是1024,所以这次并发数控制在900。

尽管这一个结果在实际的网站中由于各方面因素致使,可能差异没这么大,例如受限于数据库的性能等等的问题。但对咱们在部署网站应用时仍是具备参考价值的。

1. JVM

1.1. 使用 Server JRE 替代JDK。

服务器上不要安装JDK,请使用 Server JRE. 服务器上根本不须要编译器,代码应该在Release服务器上完成编译打包工做。

理由:一旦服务器被控制,能够防止在其服务器上编译其余恶意代码并植入到你的程序中。

 

 

 

3. Tomcat 安全配置

3.1. 安装后初始化配置

当Tomcat完成安装后你首先要作的事情以下:

首次安装完成后当即删除webapps下面的全部代码

rm -rf /srv/apache-tomcat/webapps/*

注释或删除 tomcat-users.xml 全部用户权限,看上去以下:

# cat conf/tomcat-users.xml
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
</tomcat-users>

隐藏Tomcat版本信息

vim $CATALINA_HOME/conf/server.xml

    <Connector port="80" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
				maxThreads="8192"
				minSpareThreads="64"
				maxSpareThreads="128"
				acceptCount="128"
				enableLookups="false"
                server="Neo App Srv 1.0"/>



# curl -I http://localhost:8080/
HTTP/1.1 400 Bad Request
Transfer-Encoding: chunked
Date: Thu, 20 Oct 2011 09:51:55 GMT
Connection: close
Server: Neo App Srv 1.0

服务器信息已经被改成 Server: Neo App Srv 1.0

3.2. 启动用户与端口

不要使用root用户启动tomcat,Java程序与C程序不一样。nginx,httpd 使用root用户启动守护80端口,子进程/线程会经过setuid(),setgid()两个函数切换到普通用户。即父进程全部者是root用户,子进程与多线程全部者是一个非root用户,这个用户没有shell,没法经过ssh与控制台登录系统,Java 的JVM 是与系统无关的,是创建在OS之上的,你使用什么用户启动Tomcat,那麽Tomcat 就会继承该全部者的权限。

这形成了一个问题,Linux系统小于1024的端口只有root可使用,这也是为何Tomcat默认端口是8080。若是你想使用80端口只能使用root启动Tomcat。这有带来了不少安全问题。

解决方案是建立一个不一样用户,如:

groupadd -g 80 daemon
adduser -o --home /daemon --shell /sbin/nologin --uid 80 --gid 80 -c "Web Server" daemon

注意 /sbin/nologin , 意味着该用户不能登陆,同时我也没有给它指定密码,这个用户只能用于启动tomcat

chown daemon:daemon -R /srv/*
su - daemon -c "/srv/apache-tomcat/bin/startup.sh"

接下来解决80端口问题, 思路就是80去调用8080,或者映射端口。

下面是影射方案,80 跳转 8080

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

取消跳转
iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

查看规则
iptables -t nat -L

另外一个就是从80请求去调用8080的方案

这个方案能够在 Tomcat 前段增长反向代理,例如:Nginx,Apache,Squid,Varnish或者F5, Array这类设备等等

3.3. 应用程序安全

关闭war自动部署 unpackWARs="false" autoDeploy="false"。防止被植入木马等恶意程序

应用程序部署与tomcat启动,不能使用同一个用户。

个人tomcat 安装在 /srv目录下,Tomcat启动用户为daemon; 应用程序放在/www目录下www全部者是www用户。这样的目的是一旦tomcat被植入web shell程序,它将不能建立或编辑/www目录下面的任何内容。

adduser --home /www -c "Web Application" www

3.4. JSESSIONID

修改 Cookie 变量 JSESSIONID, 这个cookie 是用于维持Session关系。建议你改成PHPSESSID。

tomcat-user.xm 安全

查看进程账户应为专用非root账号。

查看tomcat配置文件server.xml是否有设置connectionTimeout值,

符合:设置帐户自动登出。

查看配置文件是否设置ip登录范围限制

符合:配置文件中含有登录ip限制。

不符合:没设置登录ip限制。

检查tomcat/conf/web.xml配置文件,查看是否含有以下配置

复制代码
<security-constraint>    
    <web-resource-collection>    
       <url-pattern>/*</url-pattern>    
       <http-method>PUT</http-method>    
    <http-method>DELETE</http-method>    
    <http-method>HEAD</http-method>    
    <http-method>OPTIONS</http-method>    
    <http-method>TRACE</http-method>
    </web-resource-collection>    
       <auth-constraint>    
       </auth-constraint>    
    </security-constraint>    
    <login-config>    
        <auth-method>BASIC</auth-method> 
复制代码
相关文章
相关标签/搜索