Tomcat 深度解析_02
5.Web 应用配置
web.xml 是web应用的描述文件, 它支持的元素及属性来自于Servlet 规范定义 。 在Tomcat 中, Web 应用的描述信息包括 tomcat/conf/web.xml 中默认配置 以及 Web 应用 WEB-INF/web.xml 下的定制配置。
5.1 ServletContext 初始化参数
我们可以通过<context-param> 添加ServletContext 初始化参数,它配置了一个键值对,这样我们可以在应用程序中使用 javax.servlet.ServletContext.getInitParameter()方法获取参数。
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext-*.xml</param-value><description>Spring Config File Location</description> </context-param>
5.2 会话配置
<session-config> 用于配置Web应用会话,包括 超时时间、Cookie配置以及会话追踪模式。它将覆盖server.xml 和 context.xml 中的配置。
<session-config><session-timeout>30</session-timeout><cookie-config><name>JESSIONID</name><domain>www.itcast.cn</domain><path>/</path><comment>Session Cookie</comment><http-only>true</http-only><secure>false</secure><max-age>3600</max-age></cookie-config><tracking-mode>COOKIE</tracking-mode></session-config>
配置解析:
1) session-timeout : 会话超时时间,单位 分钟2) cookie-config: 用于配置会话追踪Cookiename:Cookie的名称domain:Cookie的域名path:Cookie的路径comment:注释http-only:cookie只能通过HTTP方式进行访问,JS无法读取或修改,此项可以增加网站访问的安全性。secure:此cookie只能通过HTTPS连接传递到服务器,而HTTP 连接则不会传递该信息。注意是从浏览器传递到服务器,服务器端的Cookie对象不受此项影响。max-age:以秒为单位表示cookie的生存期,默认为-1表示是会话Cookie,浏览器关闭时就会消失。3) tracking-mode :用于配置会话追踪模式,Servlet3.0版本中支持的追踪模式:COOKIE、URL、SSLA. COOKIE : 通过HTTP Cookie 追踪会话是最常用的会话追踪机制, 而且Servlet规范也要求所有的Servlet规范都需要支持Cookie追踪。B. URL : URL重写是最基本的会话追踪机制。当客户端不支持Cookie时,可以采用URL重写的方式。当采用URL追踪模式时,请求路径需要包含会话标识信息,Servlet容器会根据路径中的会话标识设置请求的会话信息。如: http://www.myserver.com/user/index.html;jessionid=1234567890。C. SSL : 对于SSL请求, 通过SSL会话标识确定请求会话标识。
5.3 Servlet配置
Servlet 的配置主要是两部分, servlet 和 servlet-mapping :
<servlet><servlet-name>myServlet</servlet-name><servlet-class>cn.itcast.web.MyServlet</servlet-class><init-param><param-name>fileName</param-name><param-value>init.conf</param-value></init-param><load-on-startup>1</load-on-startup><enabled>true</enabled> </servlet><servlet-mapping><servlet-name>myServlet</servlet-name><url-pattern>*.do</url-pattern><url-pattern>/myservet/*</url-pattern> </servlet-mapping>
配置说明:
1) servlet-name : 指定servlet的名称, 该属性在web.xml中唯一。2) servlet-class : 用于指定servlet类名3) init-param: 用于指定servlet的初始化参数, 在应用中可以通过 HttpServlet.getInitParameter 获取。 4) load-on-startup: 用于控制在Web应用启动时,Servlet的加载顺序。 值小于0,web应用启动时,不加载该servlet, 第一次访问时加载。5) enabled: true , false 。 若为false ,表示Servlet不处理任何请求。6) url-pattern: 用于指定URL表达式,一个 servlet-mapping可以同时配置多个 url-pattern。
Servlet 中文件上传配置:
<servlet><servlet-name>uploadServlet</servlet-name><servlet-class>cn.itcast.web.UploadServlet</servlet-class><multipart-config><location>C://path</location><max-file-size>10485760</max-file-size><max-request-size>10485760</max-request-size><file-size-threshold>0</file-size-threshold></multipart-config> </servlet>
配置说明:
1) location:存放生成的文件地址。2) max-file-size:允许上传的文件最大值。 默认值为-1, 表示没有限制。3) max-request-size:针对该 multi/form-data 请求的最大数量,默认值为-1, 表示无限制。4) file-size-threshold:当数量量大于该值时, 内容会被写入文件。
5.4 Listener配置
Listener用于监听servlet中的事件,例如context、request、session对象的创建、修改、删除,并触发响应事件。Listener是观察者模式的实现,在servlet中主要用于对context、request、session对象的生命周期进行监控。在servlet2.5规范中共定义了8中Listener。在启动时,ServletContextListener 的执行顺序与web.xml 中的配置顺序一致, 停止时执行顺序相反。
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
5.5 Filter配置
filter 用于配置web应用过滤器, 用来过滤资源请求及响应。 经常用于认证、日志、加密、数据转换等操作, 配置如下:
<filter><filter-name>myFilter</filter-name><filter-class>cn.itcast.web.MyFilter</filter-class><async-supported>true</async-supported><init-param><param-name>language</param-name><param-value>CN</param-value></init-param> </filter><filter-mapping><filter-name>myFilter</filter-name><url-pattern>/*</url-pattern> </filter-mapping>
配置说明:
1) filter-name: 用于指定过滤器名称,在web.xml中,过滤器名称必须唯一。2) filter-class : 过滤器的全限定类名, 该类必须实现Filter接口。3) async-supported: 该过滤器是否支持异步4) init-param :用于配置Filter的初始化参数, 可以配置多个, 可以通过FilterConfig.getInitParameter获取5) url-pattern: 指定该过滤器需要拦截的URL。
5.6 欢迎页面配置
welcome-file-list 用于指定web应用的欢迎文件列表。
<welcome-file-list><welcome-file>index.html</welcome-file><welcome-file>index.htm</welcome-file><welcome-file>index.jsp</welcome-file> </welcome-file-list>
尝试请求的顺序,从上到下。
5.7 错误页面配置
error-page 用于配置Web应用访问异常时定向到的页面,支持HTTP响应码和异常类两种形式。
<error-page><error-code>404</error-code><location>/404.html</location> </error-page> <error-page><error-code>500</error-code><location>/500.html</location> </error-page> <error-page><exception-type>java.lang.Exception</exception-type><location>/error.jsp</location> </error-page>
6.Tomcat 管理配置
从早期的Tomcat版本开始,就提供了Web版的管理控制台,他们是两个独立的Web应用,位于webapps目录下。Tomcat 提供的管理应用有用于管理的Host的host-manager和用于管理Web应用的manager。
6.1 host-manager
Tomcat启动之后,可以通过 http://localhost:8080/host-manager/html 访问该Web应用。 host-manager 默认添加了访问权限控制,当打开网址时,需要输入用户名和密码(conf/tomcat-users.xml中配置) 。所以要想访问该页面,需要在conf/tomcat-users.xml 中配置,并分配对应的角色:
1) admin-gui:用于控制页面访问权限
2) admin-script:用于控制以简单文本的形式进行访问
配置如下:
<role rolename="admin-gui"/> <role rolename="admin-script"/> <user username="itcast" password="itcast" roles="admin-script,admin-gui"/>
登录:
界面:
6.2 manager
manager的访问地址为 http://localhost:8080/manager, 同样, manager也添加了页面访问控制,因此我们需要为登录用户分配角色为:
<role rolename="manager-gui"/> <role rolename="manager-script"/> <user username="itcast" password="itcast" roles="admin-script,admin-gui,manager-gui,manager-script"/>
界面:
Server Status
7.JVM 配置
最常见的JVM配置当属内存分配,因为在绝大多数情况下,JVM默认分配的内存可能不能够满足我们的需求,特别是在生产环境,此时需要手动修改Tomcat启动时的内存参数分配。
7.1 JVM内存模型图
7.2 JVM配置选项
windows 平台(catalina.bat):
set JAVA_OPTS=-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:SurvivorRatio=8
linux 平台(catalina.sh):
JAVA_OPTS="-server -Xms1024m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:SurvivorRatio=8"
参数说明 :
序号 | 参数 | 含义 |
---|---|---|
1 | -Xms | 堆内存的初始大小 |
2 | -Xmx | 堆内存的最大大小 |
3 | -Xmn | 新生代的内存大小,官方建议是整个堆得3/8。 |
4 | -XX:MetaspaceSize | 元空间内存初始大小, 在JDK1.8版本之前配置为 -XX:PermSize(永久代) |
5 | -XX:MaxMetaspaceSize | 元空间内存最大大小, 在JDK1.8版本之前配置为 -XX:MaxPermSize(永久代) |
6 | -XX:InitialCodeCacheSize -XX:ReservedCodeCacheSize | 代码缓存区大小 |
7 | -XX:NewRatio | 设置新生代和老年代的相对大小比例。这种方式的优点是新生代大小会随着整个堆大小动态扩展。如 -XX:NewRatio=3 指定老年代 / 新生代为 3/1。 老年代占堆大小的 3/4,新生代占 1/4 。 |
8 | -XX:SurvivorRatio | 指定伊甸园区 (Eden) 与幸存区大小比例。如 -XX:SurvivorRatio=10 表示伊甸园区 (Eden) 是 幸存区 To 大小的 10 倍 (也是幸存区 From 的 10 倍)。 所以, 伊甸园区 (Eden) 占新生代大小的 10/12, 幸存区 From 和幸存区 To 每个占新生代的 1/12 。 注意, 两个幸存区永远是一样大的。 |
配置之后, 重新启动Tomcat ,访问 :
8. Tomcat 集群
8.1 简介
由于单台Tomcat的承载能力是有限的,当我们的业务系统用户量比较大,请求压力比较大时,单台Tomcat是扛不住的,这个时候,就需要搭建Tomcat的集群,而目前比较流程的做法就是通过Nginx来实现Tomcat集群的负载均衡。
8.2 环境准备
8.2.2 准备Tomcat
在服务器上, 安装两台tomcat, 然后分别改Tomcat服务器的端口号 :
8005 ---------> 8015 ---------> 80258080 ---------> 8888 ---------> 9999 8009 ---------> 8019 ---------> 8029
8.2.3 安装配置Nginx
在当前服务器上 , 安装Nginx , 然后再配置Nginx, 配置nginx.conf :
upstream serverpool{server localhost:8888;server localhost:9999; }server {listen 99;server_name localhost;location / { proxy_pass http://serverpool/; } }
8.3 负载均衡策略
1). 轮询
最基本的配置方法,它是upstream模块默认的负载均衡默认策略。每个请求会按时间顺序逐一分配到不同的后端服务器。
upstream serverpool{server localhost:8888;server localhost:9999; }
参数说明:
参数 | 描述 |
---|---|
fail_timeout | 与max_fails结合使用 |
max_fails | 设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了 |
fail_time | 服务器会被认为停机的时间长度,默认为10s |
backup | 标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里 |
down | 标记服务器永久停机了 |
2). weight权重
权重方式,在轮询策略的基础上指定轮询的几率。
upstream serverpool{server localhost:8888 weight=3;server localhost:9999 weight=1; }
weight参数用于指定轮询几率,weight的默认值为1;weight的数值与访问比率成正比,比如8888服务器上的服务被访问的几率为9999服务器的三倍。
此策略比较适合服务器的硬件配置差别比较大的情况。
3). ip_hash
指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。
upstream serverpool{ip_hash;server 192.168.192.133:8080;server 192.168.192.137:8080; }
8.4 Session共享方案
在Tomcat集群中,如果应用需要用户进行登录,那么这个时候,用于tomcat做了负载均衡,则用户登录并访问应用系统时,就会出现问题 。
解决上述问题, 有以下几种方案:
8.4.1 ip_hash 策略
一个用户发起的请求,只会请求到tomcat1上进行操作,另一个用户发起的请求只在tomcat2上进行操作 。那么这个时候,同一个用户发起的请求,都会通过nginx的ip_hash策略,将请求转发到其中的一台Tomcat上。
8.4.2 Session复制
在servlet_demo01 工程中 , 制作session.jsp页面,分别将工程存放在两台 tomcat 的 webapps/ 目录下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Title</title> </head> <body>TOMCAT - 9999 : <br/>sessionID : <%= session.getId()%><br/><%Object loginUser = session.getAttribute("loginUser");if(loginUser != null && loginUser.toString().length()>0){out.println("session 有值, loginUser = " + loginUser);}else{session.setAttribute("loginUser","ITCAST");out.println("session 没有值");}%></body> </html>
通过nginx访问 , http://localhost:99/demo01/session.jsp ,访问到的两台Tomcat出现的sessionID是不一样的:
上述现象,则说明两台Tomcat的Session各是各的,并没有进行同步,这在集群环境下是存在问题的。
Session同步的配置如下:
1) 在Tomcat的conf/server.xml 配置如下:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
2) 在Tomcat部署的应用程序 servlet_demo01 的web.xml 中加入如下配置 :
<distributable/>
3) 配置完毕之后, 再次重启两个 Tomcat服务。
上述方案,适用于较小的集群环境(节点数不超过4个),如果集群的节点数比较多的话,通过这种广播的形式来完成Session的复制,会消耗大量的网络带宽,影响服务的性能。
8.4.3 SSO-单点登录
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,也是用来解决集群环境Session共享的方案之一 。
9. Tomcat 安全
9.1 配置安全
1) 删除webapps目录下的所有文件,禁用tomcat管理界面;
2) 注释或删除tomcat-users.xml文件内的所有用户权限;
3) 更改关闭tomcat指令或禁用;
tomcat的server.xml中定义了可以直接关闭 Tomcat 实例的管理端口(默认8005)。可以通过 telnet 连接上该端口之后,输入 SHUTDOWN (此为默认关闭指令)即可关闭 Tomcat 实例(注意,此时虽然实例关闭了,但是进程还是存在的)。由于默认关闭Tomcat 的端口和指令都很简单。默认端口为8005,指令为SHUTDOWN 。
方案一:
更改端口号和指令: <Server port="8456" shutdown="itcast_shut">
方案二:
禁用8005端口: <Server port="-1" shutdown="SHUTDOWN">
4) 定义错误页面
在webapps/ROOT目录下定义错误页面 404.html,500.html;
然后在tomcat/conf/web.xml中进行配置 , 配置错误页面:
<error-page> <error-code>404</error-code> <location>/404.html</location> </error-page><error-page> <error-code>500</error-code> <location>/500.html</location> </error-page>
这样配置之后,用户在访问资源时出现404,500这样的异常,就能看到我们自定义的错误页面,而不会看到异常的堆栈信息,提高了用户体验,也保障了服务的安全性。
9.2 应用安全
在大部分的Web应用中,特别是一些后台应用系统,都会实现自己的安全管理模块(权限模块),用于控制应用系统的安全访问,基本包含两个部分:认证(登录/单点登录)和授权(功能权限、数据权限)两个部分。对于当前的业务系统,可以自己做一套适用于自己业务系统的权限模块,也有很多的应用系统直接使用一些功能完善的安全框架,将其集成到我们的web应用中,如:SpringSecurity、Apache Shiro等。
9.3 传输安全
9.3.1 HTTPS介绍
HTTPS的全称是超文本传输安全协议(Hypertext Transfer Protocol Secure),是一种网络安全传输协议。在HTTP的基础上加入SSL/TLS来进行数据加密,保护交换数据不被泄露、窃取。
SSL 和 TLS 是用于网络通信安全的加密协议,它允许客户端和服务器之间通过安全链接通信。SSL 协议的3个特性:
1) 保密:通过SSL链接传输的数据时加密的。
2) 鉴别:通信双方的身份鉴别,通常是可选的,单至少有一方需要验证。
3) 完整性:传输数据的完整性检查。
从性能角度考虑,加解密是一项计算昂贵的处理,因为尽量不要将整个Web应用采用SSL链接, 实际部署过程中, 选择有必要进行安全加密的页面(存在敏感信息传输的页面)采用SSL通信。
HTTPS和HTTP的区别主要为以下四点:
1) HTTPS协议需要到证书颁发机构CA申请SSL证书, 然后与域名进行绑定,HTTP不用申请证书;
2) HTTP是超文本传输协议,属于应用层信息传输,HTTPS 则是具有SSL加密传安全性传输协议,对数据的传输进行加密,相当于HTTP的升级版;
3) HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是8080,后者是8443。
4) HTTP的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。
HTTPS协议优势:
1) 提高网站排名,有利于SEO。谷歌已经公开声明两个网站在搜索结果方面相同,如果一个网站启用了SSL,它可能会获得略高于没有SSL网站的等级,而且百度也表明对安装了SSL的网站表示友好。因此,网站上的内容中启用SSL都有明显的SEO优势。
2) 隐私信息加密,防止流量劫持。特别是涉及到隐私信息的网站,互联网大型的数据泄露的事件频发发生,网站进行信息加密势在必行。
3) 浏览器受信任。 自从各大主流浏览器大力支持HTTPS协议之后,访问HTTP的网站都会提示“不安全”的警告信息。
9.3.2 Tomcat支持HTTPS
1) 生成秘钥库文件。
keytool -genkey -alias tomcat -keyalg RSA -keystore tomcatkey.keystore
输入对应的密钥库密码, 秘钥密码等信息之后,会在当前文件夹中出现一个秘钥库文件:tomcatkey.keystore
2) 将秘钥库文件 tomcatkey.keystore 复制到tomcat/conf 目录下。
3) 配置tomcat/conf/server.xml
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"maxThreads="150" schema="https" secure="true" SSLEnabled="true"><SSLHostConfig certificateVerification="false"><Certificate certificateKeystoreFile="D:/DevelopProgramFile/apache-tomcat-8.5.42-windows-x64/apache-tomcat-8.5.42/conf/tomcatkey.keystore" certificateKeystorePassword="itcast" type="RSA" /></SSLHostConfig> </Connector>
4)访问Tomcat ,使用https协议。
10. Tomcat 性能调优
10.1 Tomcat 性能测试
对于系统性能,用户最直观的感受就是系统的加载和操作时间,即用户执行某项操作的耗时。从更为专业的角度上讲,性能测试可以从以下两个指标量化。
1). 响应时间:如上所述,为执行某个操作的耗时。大多数情况下,我们需要针对同一个操作测试多次,以获取操作的平均响应时间。
2). 吞吐量:即在给定的时间内,系统支持的事务数量,计算单位为 TPS。
通常情况下,我们需要借助于一些自动化工具来进行性能测试,因为手动模拟大量用户的并发访问几乎是不可行的,而且现在市面上也有很多的性能测试工具可以使用,如:ApacheBench、ApacheJMeter、WCAT、WebPolygraph、LoadRunner。
我们课程上主要介绍两款免费的工具:ApacheBench。
10.1.1 ApacheBench
ApacheBench(ab)是一款ApacheServer基准的测试工具,用户测试Apache Server的服务能力(每秒处理请求数),它不仅可以用户Apache的测试,还可以用于测试Tomcat、Nginx、lighthttp、IIS等服务器。
1) 安装
yum install httpd-tools
2) 查看版本号
ab -V
3) 部署war包, 准备环境
A. 在Linux系统上安装Tomcat上传 : alt + p -------> put D:/apache-tomcat-8.5.42.tar.gz解压 : tar -zxvf apache-tomcat-8.5.42.tar.gz -C /usr/local修改端口号:8005 , 8080 , 8009B. 将资料中的war包上传至Tomcat的webapps下上传: alt + p ---------> put D:/ROOT.war启动Tomcat解压C. 导入SQL脚本 , 准备环境
4) 测试性能
ab -n 1000 -c 100 -p data.json -T application/json http://localhost:9000/course/search.do?page=1&pageSize=10
参数说明
参数 | 含义描述 |
---|---|
-n | 在测试会话中所执行的请求个数,默认只执行一次请求 |
-c | 一次产生的请求个数,默认一次一个 |
-p | 包含了需要POST的数据文件 |
-t | 测试所进行的最大秒数,默认没有时间限制 |
-T | POST数据所需要使用的Content-Type头信息 |
-v | 设置显示信息的详细程度 |
-w | 以HTML表的格式输出结果,默认是白色背景的两列宽度的一张表 |
结果说明
指标 | 含义 |
---|---|
Server Software | 服务器软件 |
Server Hostname | 主机名 |
Server Port | 端口号 |
Document Path | 测试的页面 |
Document Length | 测试的页面大小 |
Concurrency Level | 并发数 |
Time taken for tests | 整个测试持续的时间 |
Complete requests | 完成的请求数量 |
Failed requests | 失败的请求数量,这里的失败是指请求的连接服务器、发送数据、接收数据等环节发生异常,以及无响应后超时的情况。 |
Write errors | 输出错误数量 |
Total transferred | 整个场景中的网络传输量,表示所有请求的响应数据长度总和,包括每个http响应数据的头信息和正文数据的长度。 |
HTML transferred | 整个场景中的HTML内容传输量,表示所有请求的响应数据中正文数据的总和 |
Requests per second | 每秒钟平均处理的请求数(相当于 LR 中的 每秒事务数)这便是我们重点关注的吞吐率,它等于:Complete requests / Time taken for tests |
Time per request | 每个线程处理请求平均消耗时间(相当于 LR 中的 平均事务响应时间)用户平均请求等待时间 |
Transfer rate | 平均每秒网络上的流量 |
Percentage of the requests served within a certain time (ms) | 指定时间里,执行的请求百分比 |
重要指标
参数 | 指标说明 |
---|---|
Requests per second | 吞吐率:服务器并发处理能力的量化描述,单位是reqs/s,指的是在某个并发用户数下单位时间内处理的请求数。某个并发用户数下单位时间内能处理的最大请求数,称之为最大吞吐率。 这个数值表示当前机器的整体性能,值越大越好。 |
Time per request | 用户平均请求等待时间:从用户角度看,完成一个请求所需要的时间 |
Time per request:across all concurrent requests | 服务器平均请求等待时间:服务器完成一个请求的时间 |
Concurrency Level | 并发用户数 |
10.2 Tomcat 性能优化
10.2.1 JVM参数调优
Tomcat是一款Java应用,那么JVM的配置便与其运行性能密切相关,而JVM优化的重点则集中在内存分配和GC策略的调整上,因为内存会直接影响服务的运行效率和吞吐量, JVM垃圾回收机制则会不同程度地导致程序运行中断。可以根据应用程序的特点,选择不同的垃圾回收策略,调整JVM垃圾回收策略,可以极大减少垃圾回收次数,提升垃圾回收效率,改善程序运行性能。
1) JVM内存参数
参数 | 参数作用 | 优化建议 |
---|---|---|
-server | 启动Server,以服务端模式运行 | 服务端模式建议开启 |
-Xms | 最小堆内存 | 建议与-Xmx设置相同 |
-Xmx | 最大堆内存 | 建议设置为可用内存的80% |
-XX:MetaspaceSize | 元空间初始值 | |
-XX:MaxMetaspaceSize | 元空间最大内存 | 默认无限 |
-XX:MaxNewSize | 新生代最大内存 | 默认16M |
-XX:NewRatio | 年轻代和老年代大小比值,取值为整数,默认为2 | 不建议修改 |
-XX:SurvivorRatio | Eden区与Survivor区大小的比值,取值为整数,默认为8 | 不建议修改 |
JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:SurvivorRatio=8"
jmap -heap 51421
2) GC策略
JVM垃圾回收性能有以下两个主要的指标:
吞吐量:工作时间(排除GC时间)占总时间的百分比, 工作时间并不仅是程序运行的时间,还包含内存分配时间。
暂停时间:测试时间段内,由垃圾回收导致的应用程序停止响应次数/时间。
在Sun公司推出的HotSpotJVM中, 包含以下几种不同类型的垃圾收集器:
垃圾收集器 | 含义说明 |
---|---|
串行收集器 (Serial Collector) | 采用单线程执行所有的垃圾回收工作, 适用于单核CPU服务器,无法利用多核硬件的优势 |
并行收集器 (Parallel Collector) | 又称为吞吐量收集器, 以并行的方式执行年轻代的垃圾回收, 该方式可以显著降低垃圾回收的开销(指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态)。适用于多处理器或多线程硬件上运行的数据量较大的应用 |
并发收集器 (Concurrent Collector) | 以并发的方式执行大部分垃圾回收工作,以缩短垃圾回收的暂停时间。适用于那些响应时间优先于吞吐量的应用, 因为该收集器虽然最小化了暂停时间(指用户线程与垃圾收集线程同时执行,但不一定是并行的,可能会交替进行), 但是会降低应用程序的性能 |
CMS收集器 (Concurrent Mark Sweep Collector) | 并发标记清除收集器, 适用于那些更愿意缩短垃圾回收暂停时间并且负担的起与垃圾回收共享处理器资源的应用 |
G1收集器 (Garbage-First Garbage Collector) | 适用于大容量内存的多核服务器, 可以在满足垃圾回收暂停时间目标的同时, 以最大可能性实现高吞吐量(JDK1.7之后) |
不同的应用程序, 对于垃圾回收会有不同的需求。 JVM 会根据运行的平台、服务器资源配置情况选择合适的垃圾收集器、堆内存大小及运行时编译器。如无法满足需求, 参考以下准则:
A. 程序数据量较小,选择串行收集器。
B. 应用运行在单核处理器上且没有暂停时间要求, 可交由JVM自行选择或选择串行收集器。
C. 如果考虑应用程序的峰值性能, 没有暂停时间要求, 可以选择并行收集器。
D. 如果应用程序的响应时间比整体吞吐量更重要, 可以选择并发收集器。
查看Tomcat中的默认的垃圾收集器:
1). 在tomcat/bin/catalina.sh的配置中, 加入如下配置
JAVA_OPTS=" -Djava.rmi.server.hostname=192.168.192.138 -Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.rmi.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
2). 打开 jconsole , 查看远程的tomcat的概要信息
连接远程tomcat
GC参数:
参数 | 描述 |
---|---|
-XX:+UseSerialGC | 启用串行收集器 |
-XX:+UseParallelGC | 启用并行垃圾收集器,配置了该选项,那么 -XX:+UseParallelOldGC默认启用 |
-XX:+UseParallelOldGC | FullGC 采用并行收集,默认禁用。如果设置了 -XX:+UseParallelGC则自动启用 |
-XX:+UseParNewGC | 年轻代采用并行收集器,如果设置了 -XX:+UseConcMarkSweepGC选项,自动启用 |
-XX:ParallelGCThreads | 年轻代及老年代垃圾回收使用的线程数。默认值依赖于JVM使用的CPU个数 |
-XX:+UseConcMarkSweepGC | 对于老年代,启用CMS垃圾收集器。 当并行收集器无法满足应用的延迟需求是,推荐使用CMS或G1收集器。 启用该选项后, -XX:+UseParNewGC 自动启用。 |
-XX:+UseG1GC | 启用G1收集器。 G1是服务器类型的收集器, 用于多核、大内存的机器。它在保持高吞吐量的情况下,高概率满足GC暂停时间的目标。 |
我们也可以在测试的时候,将JVM参数调整之后,将GC的信息打印出来,便于为我们进行参数调整提供依据,具体参数如下:
选项 | 描述 |
---|---|
-XX:+PrintGC | 打印每次GC的信息 |
-XX:+PrintGCApplicationConcurrentTime | 打印最后一次暂停之后所经过的时间, 即响应并发执行的时间 |
-XX:+PrintGCApplicationStoppedTime | 打印GC时应用暂停时间 |
-XX:+PrintGCDateStamps | 打印每次GC的日期戳 |
-XX:+PrintGCDetails | 打印每次GC的详细信息 |
-XX:+PrintGCTaskTimeStamps | 打印每个GC工作线程任务的时间戳 |
-XX:+PrintGCTimeStamps | 打印每次GC的时间戳 |
在bin/catalina.sh的脚本中 , 追加如下配置 :
JAVA_OPTS="-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails"
10.2.2 Tomcat 配置调优
调整tomcat/conf/server.xml 中关于链接器的配置可以提升应用服务器的性能。
参数 | 说明 |
---|---|
maxConnections | 最大连接数,当到达该值后,服务器接收但不会处理更多的请求, 额外的请求将会阻塞直到连接数低于maxConnections 。可通过ulimit -a 查看服务器限制。对于CPU要求更高(计算型)时,建议不要配置过大; 对于CPU要求不是特别高时,建议配置在2000左右(受服务器性能影响)。 当然这个需要服务器硬件的支持 |
maxThreads | 最大线程数,需要根据服务器的硬件情况,进行一个合理的设置 |
acceptCount | 最大排队等待数,当服务器接收的请求数量到达maxConnections ,此时Tomcat会将后面的请求,存放在任务队列中进行排序, acceptCount指的就是任务队列中排队等待的请求数 。 一台Tomcat的最大的请求处理数量,是maxConnections+acceptCount。 |
11. Tomcat 附加功能
11.1 WebSocket
11.1.1 WebSocket介绍
WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双向通信的通道,比如说,服务器可以在任意时刻发送消息给浏览器。
为什么传统的HTTP协议不能做到WebSocket实现的功能?这是因为HTTP协议是一个请求-响应协议,请求必须先由浏览器发给服务器,服务器才能响应这个请求,再把数据发送给浏览器。换句话说,浏览器不主动请求,服务器是没法主动发数据给浏览器的。
这样一来,要在浏览器中搞一个实时聊天,或者在线多人游戏的话就没法实现了,只能借助Flash这些插件。也有人说,HTTP协议其实也能实现啊,比如用轮询或者Comet。轮询是指浏览器通过JavaScript启动一个定时器,然后以固定的间隔给服务器发请求,询问服务器有没有新消息。这个机制的缺点一是实时性不够,二是频繁的请求会给服务器带来极大的压力。
Comet本质上也是轮询,但是在没有消息的情况下,服务器先拖一段时间,等到有消息了再回复。这个机制暂时地解决了实时性问题,但是它带来了新的问题:以多线程模式运行的服务器会让大部分线程大部分时间都处于挂起状态,极大地浪费服务器资源。另外,一个HTTP连接在长时间没有数据传输的情况下,链路上的任何一个网关都可能关闭这个连接,而网关是我们不可控的,这就要求Comet连接必须定期发一些ping数据表示连接“正常工作”。
以上两种机制都治标不治本,所以,HTML5推出了WebSocket标准,让浏览器和服务器之间可以建立无限制的全双工通信,任何一方都可以主动发消息给对方。WebSocket并不是全新的协议,而是利用了HTTP协议来建立连接。我们来看看WebSocket连接是如何创建的。
首先,WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求,格式如下:
该请求和普通的HTTP请求有几点不同:
GET请求的地址不是类似 http://,而是以 ws:// 开头的地址;
请求头 Connection: Upgrade 和 请求头 Upgrade: websocket 表示这个连接将要被转换为 WebSocket 连接;
Sec-WebSocket-Key 是用于标识这个连接, 是一个BASE64编码的密文, 要求服务端响应一个对应加密的Sec-WebSocket-Accept头信息作为应答;
Sec-WebSocket-Version 指定了WebSocket的协议版本;
HTTP101 状态码表明服务端已经识别并切换为WebSocket协议 , Sec-WebSocket-Accept是服务端与客户端一致的秘钥计算出来的信息。
11.2.2 Tomcat的 Websocket
Tomcat的7.0.5 版本开始支持WebSocket,并且实现了Java WebSocket规范(JSR356), 而在7.0.5版本之前(7.0.2之后)则采用自定义API, 即WebSocketServlet实现。
Java WebSocket应用由一系列的WebSocketEndpoint组成。Endpoint 是一个java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口, 就像Servlet之与http请求一样。
我们可以通过两种方式定义Endpoint:
1). 第一种是编程式, 即继承类 javax.websocket.Endpoint并实现其方法。
2). 第二种是注解式, 即定义一个POJO, 并添加 @ServerEndpoint相关注解。
Endpoint实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。在Endpoint接口中明确定义了与其生命周期相关的方法, 规范实现者确保生命周期的各个阶段调用实例的相关方法。生命周期方法如下:
方法 | 含义描述 | 注解 |
---|---|---|
onClose | 当会话关闭时调用。 | @OnClose |
onOpen | 当开启一个新的会话时调用, 该方法是客户端与服务端握手成功后调用的方法。 | @OnOpen |
onError | 当连接过程中异常时调用。 | @OnError |
通过为Session添加MessageHandler消息处理器来接收消息,当采用注解方式定义Endpoint时,我们还可以通过 @OnMessage 注解指定接收消息的方法。发送消息则由RemoteEndpoint 完成, 其实例由Session维护, 根据使用情况, 我们可以通过Session.getBasicRemote获取同步消息发送的实例 , 然后调用其sendXxx()方法就可以发送消息, 可以通过Session.getAsyncRemote 获取异步消息发送实例。
11.2.3 WebSocket DEMO案例
11.2.3.1 需求
通过 websocket 实现一个简易的聊天室功能 ;
1). 登录聊天室
2) 登陆之后,进入聊天界面进行聊天
用户 Deng 的界面:
用户 ICAST 的界面 :
11.2.3.2 实现流程
11.2.3.3 消息格式
客户端-->服务端 : {"fromName":"Deng","toName":"HEIMA","content":"约会呀"}
服务端-->客户端 :
①. 如果type为user , 则说明返回的是用户列表
{"data":"HEIMA,Deng,ITCAST","toName":"","fromName":"","type":"user"}
②. 如果type为message , 则说明返回的是消息内容
{"data":"你好","toName":"HEIMA","fromName":"Deng","type":"message"}
11.2.3.4 功能实现
1) 创建项目, 导入项目依赖。
2) 引入静态资源文件。
3) 定义一个登陆的servlet
获取页面传递的用户名和密码 , 只要传递的聊天室密码为123456 , 则认为正确, 允许登录 .
@WebServlet(name = "loginServlet",urlPatterns = {"/login"}) public class LoginServlet extends HttpServlet {private static final String PASSWORD = "123456";@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setCharacterEncoding("UTF-8");String username = req.getParameter("username");String password = req.getParameter("password");Map resultMap = new HashMap();if(password != null && password.equals(PASSWORD)){req.getSession().setAttribute("username",username);resultMap.put("success",true);resultMap.put("message","登录成功");}else{resultMap.put("success",false);resultMap.put("message","登录失败");}resp.getWriter().write(JSON.toJSONString(resultMap));}}
4) 定义配置类,便于在WS中获取HttpSession
/*** 获取HttpSession对象的配置类*/ public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {/*** 获取到HttpSession , 并将其存储在 ServerEndpointConfig对象中.* @param config* @param request* @param response*/@Overridepublic void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {HttpSession httpSession = (HttpSession) request.getHttpSession();config.getUserProperties().put(HttpSession.class.getName(), httpSession);}}
5) 定义WebSocket的服务端程序
@ServerEndpoint(value = "/websocket", configurator = GetHttpSessionConfigurator.class) public class ChatServlet {//该map集合用来存储所有在线用户的实例信息private static final Map<HttpSession, ChatServlet> onlineUsers = new HashMap<HttpSession, ChatServlet>();//记录在线用户数private static int onlineCount = 0;//用户的HttpSessionprivate HttpSession httpSession;//用户的WS的会话信息Sessionprivate Session session;/*** @onOpen :当开启一个新的会话时调用, 该方法是客户端与服务端握手成功后调用的方法。** 为当前Servlet中的Session赋值 , 为HttpSession赋值, 将当前的会话信息, 记录在在线用户集合中 ;** 获取到当前所有在线的用户信息 , 并且给所有的WS客户端推送消息, 在客户端更新好友列表。* 在线用户数增加1**/@OnOpenpublic void onOpen(Session session, EndpointConfig config) {this.session = session;this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());if (httpSession.getAttribute("username") != null) {onlineUsers.put(httpSession, this);}String names = getNames();String content = MessageUtil.getContent(MessageUtil.TYPE_USER, "", "", names);System.out.println("服务端给客户端广播消息: "+ content);broadcastAll(content);addOnlineCount(); //在线数加1System.out.println("有新连接加入!当前在线人数为" + onlineUsers.size());}@OnClosepublic void onClose(Session session, CloseReason closeReason) {//onlineUsers.remove(this); //从set中删除subOnlineCount(); //在线数减1System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());}/*** 接收客户端传递的消息, 并且根据消息中的toName判定当前消息给那个客户端发送* @param message* @param session* @throws Exception*/@OnMessagepublic void onMessage(String message, Session session) throws Exception {//获取客户端发送的消息,并解析Map<String, String> messageMap = JSON.parseObject(message, Map.class);String fromName = messageMap.get("fromName"); //消息来自人 的userIdString toName = messageMap.get("toName"); //消息发往人的 userIdString mapContent = messageMap.get("content");//判断是否有接收人if (toName == null || toName.isEmpty()) {return;}//如果接收人是 all , 则说明是广播消息if ("all".equals(toName)) {String content = MessageUtil.getContent(MessageUtil.TYPE_MESSAGE, fromName, "all", mapContent);broadcastAll(content);} else {//如果不是all , 则给指定用户推送消息try {String content = MessageUtil.getContent(MessageUtil.TYPE_MESSAGE, fromName, toName, mapContent);System.out.println("服务端给客户端推消息: "+ content);singleChat(fromName, toName, content);} catch (IOException e) {e.printStackTrace();}}System.out.println("来自客户端的消息:" + message);//broadcastAll(message);}private void singleChat(String fromName, String toName, String mapContent) throws IOException {boolean isExit = false;//判定收件人是否存在for (HttpSession key : onlineUsers.keySet()) {if (key.getAttribute("username").equals(toName)) {isExit = true;}}//如果存在则, 发送消息if (isExit) {for (HttpSession key : onlineUsers.keySet()) {if (key.getAttribute("username").equals(fromName) || key.getAttribute("username").equals(toName)) {onlineUsers.get(key).session.getBasicRemote().sendText(mapContent);}}}}//发送广播消息private void broadcastAll(String msg) {for (HttpSession key : onlineUsers.keySet()) {try {onlineUsers.get(key).session.getBasicRemote().sendText(msg);} catch (IOException e) {e.printStackTrace();}}}@OnErrorpublic void onError(Session session, Throwable error) {error.printStackTrace();System.out.println("发生错误");}//获取所有当前在线的用户private String getNames() {String names = "";for (HttpSession key : onlineUsers.keySet()) {String name = (String) key.getAttribute("username");names += name + ",";}String namesTemp = names.substring(0, names.length() - 1);return namesTemp;}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {ChatServlet.onlineCount++;}public static synchronized void subOnlineCount() {ChatServlet.onlineCount--;} }
Tomcat 深度解析_02相关推荐
- 推荐:微服务架构的深度解析!
通过采用微服务架构,企业最大的收益是帮助内部IT建设沿着可演进的方向发展.支持灵活扩展.降低运维成本.快速响应业务变化. 这些底层技术能力的提升让业务更加敏捷.成本可控,企业也可以从中获得技术红利和市 ...
- 《Spring源码深度解析 郝佳 第2版》SpringBoot体系分析、Starter的原理
往期博客 <Spring源码深度解析 郝佳 第2版>容器的基本实现与XML文件的加载 <Spring源码深度解析 郝佳 第2版>XML标签的解析 <Spring源码深度解 ...
- Spring源码深度解析(郝佳)-学习-源码解析-创建AOP静态代理实现(八)
继上一篇博客,我们继续来分析下面示例的 Spring 静态代理源码实现. 静态 AOP使用示例 加载时织入(Load -Time WEaving,LTW) 指的是在虚拟机载入字节码时动态织入 Aspe ...
- Spring源码深度解析:三、容器的刷新 - refresh()
一.前言 文章目录:Spring源码深度解析:文章目录 我们先通过Spring源码的整体流程,来了解Spring的工作流程是什么,接着根据这个工作流程一步一步的阅读源码 二.Spring容器的启动 p ...
- 单文件浏览器_图文并茂深度解析浏览器渲染原理,包看懂超值得收藏
在我们面试过程中,面试官经常会问到这么一个问题,那就是从在浏览器地址栏中输入URL到页面显示,浏览器到底发生了什么?这个问题看起来是老生常谈,但是这个问题回答的好坏,确实可以很好的反映出面试者知识的广 ...
- 舒工深度解析不规则场地座位二维码生成规则
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8" ...
- 今晚8点开播 | 深度解析知识图谱发展关键阶段技术脉络
作为知识图谱领域形成过程的亲历者之一,AI科技大本营此次邀请到文因互联 CEO 鲍捷,他将对知识图谱的历史渊源进行梳理,对该领域几次发展的主要技术突破做深度解析,并分析其工业落地的几个关键点.欢迎大家 ...
- 多目标跟踪算法FairMOT深度解析
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨周威@知乎 来源丨https://zhuanlan.zhihu.com/p/259356109 ...
- 深度解析工业软件:研究框架(140页)
来源:跨越鸿沟 本文多图,建议阅读6分钟. 本文为你深度解析工业软件:研究框架 工业软件的核心,不仅是"软件",更是"工业". 有什么样的工业流程,就有什么样的 ...
最新文章
- 死锁产生的原因和解锁的方法
- Science:纽约西奈山医学院房刚组定量分析真核生物DNA 6mA解析细菌污染的影响...
- Combiner合并案例
- 使用MAT(Memory Analyzer Tool)工具分析dump文件--转
- 如何在64位的Linux中运行32位的应用程序
- java开发用怎么软件开发_Java 9中的5个功能将改变您开发软件的方式(还有2个不会)...
- linux tips 技巧笔记一
- 漫步数学分析九——级数
- Python之石头剪刀布
- MySQL结果集 数据查询(重点)
- [转载] python中字典copy_python深度复制字典,copy方法与deepcopy方法
- java.util 1.8_JDK1.8源码(四)——java.util.Arrays 类
- 参数模型 非参数模型 生成模型 判别模型
- hive学习4(hive的脚本执行)
- chown、chgrp 更改文件属主属组
- 【恶搞Python】Python实现QQ连续发送信息的代码,咋就说可还刑
- tm8211的i2s协议
- java和vue实现拖拽可视化_可视化拖拽页面编辑器 一__Vue.js
- ESP8266在局域网和互联网下的使用
- Pytorch基础知识(7)单目标检测