Tomcat介绍及调优
1 Tomcat介绍
Tomcat是一个利用servlet标准建立的一个轻量级的容器,又叫中间件,他是介于客户端和服务端中间通信的容器,通常部署后端代码在上面,映射端口给客户端访问。
1.1 Http原理
- 用户请求
- 浏览器发起TCP链接请求
- 服务器接收请求并建立链接
- 浏览器生成HTTP格式的数据包
- 浏览器向服务器发送请求书举报
- 服务器解析HTTP格式的数据包
- 服务器质性请求
- 服务器生成HTTP格式的数据包
- 服务器向浏览器发送响应数据包
- 浏览器解析HTTP格式的数据包
- 浏览器呈现HTML给用户。
1.2 Tomcat整体架构
这种架构的优点是Http不直接调用业务类,而是把请求交给容器来处理,容器通过Servlet接口调用业务类,因此Servlet接口和Servlet容器的出现,达到了HTTP服务器与业务代码解耦的目的。而Servlet容器这一套规范叫做Servlet规范,Tomcat按照Servlet规范的要求实现了Servlet容器,同时它们也具有HTTP服务器的功能。而后端开发,只需要实现一个Servlet,并且把它注册到Tomcat中,剩下的事就交给Tomcat帮我们处理了。
1.3 核心功能(连接器(Connector)和 容器(Container)):
- 处理Socket连接,负责网络字节流与Request和Response对象的转化。
- 加载和管理Servlet,以及具体处理Request请求。
连接器架构
Coyote是Tomcat的连接器框架的名称,是Tomcat提供的供客户端访问的外部接口。客户端通过Coyote与服务器建立连接,发送请求并接受响应。
Coyote封装了底层的网络通信(Socket请求及响应处理),为Catalina容器提供了统一的接口。使Catalina容器与具体的请求协议及IO操作方式完全解耦。Coyote将Socket输入转换封装为Request对象,交由Catalina容器进行处理,处理完请求之后,Catalina通过Coyote提供的Response对象将结果写入输出流。
Coyote作为独立的模块,只负责具体协议和IO的相关操作,与Servlet规范没有直接的关系,因此即便是Request和Response对象也并未实现Servlet规范对应的接口,而是在Catalina容器中将他们进一步封装为ServletRequest和ServletResponse。
连接器组件
- EndPoint:Coyote的通信端点,即通信监听的接口,用来实现TCP/IP协议。
- Processor: Coyote的协议处理接口,用来实现HTTP协议。
- ProtacolHandler:Coyote协议接口,通过Endpoint和Processor实现具体的协议的处理能力,在配置server.xml要指定具体的protocolHandler,如:HTTP/1.1
- Adapter:由于协议不同,客户端发来的请求也不同,Tomcat定义了自己的Request类来存放这些请求信息。ProtocolHandler负责解析并生成Tomcat Request类。但是这个Request对象不是标准的ServletRequest,Tomcat设计者的解决方案是引入CoyoteAdapter适配器,这是适配器模式的经典写法,CoyoteAdapter负责将TomcatRequest转换为ServletRequest,再调用容器的Service方法。
Catalina容器
由下图可以看出Catalina的地位,是Tomcat的核心,Tomcat通过Coyote模块提供链接通信,通过Jasper模块提供Jsp引擎,Naming提供JNDI服务,JULI提供日志服务。
Catalina架构
如下图所示,Catalina负责管理server,而server是整个服务器,每一个服务器下面有多个Service,每个服务都包含着连接器组件和一个容器组件。
Tomcat设置了四种容器,分别是Engine,Host,Context和Wrapper。
- engine: 标识整个Catalina的Servlet引擎,用来管理多个虚拟站点,一个Service最多只能有一个Engine,但是可以包含多个Host
- Host: 代表一个虚拟主机,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可包含多个Context
- Context: 表示一个Web应用,一个WEB因公可以包含多个Wrapper
- Wrapper:表示一个Servlet。
1.4 IO模型与协议
IO模型
IO模型 | 描述 |
---|---|
BIO | 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。(8.5/9.0版本移除) |
NIO | 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 |
NIO2/AIO | 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理 |
APR | 采用Apache可移植运行库实现,是C/C++编写的类库,需要安装单独的库 |
Tomcat支持的协议
应用层协议 | 描述 |
---|---|
HTTP/1.1 | 大部分web应用的访问协议 |
AJP | 用于和WEB服务器集成,以实现对静态资源的优化以及集成部署,当前支持AJP/1.3 |
HTTP/2 | HTTP/2.0大幅度提升了WEB性能。(8.5/9.0版本后支持) |
1.5 Servlet容器工作流程
当用户请求某个资源时,HTTP服务器会用一个ServletRequest对象把用户的请求信息封装起来,然后调用Servlet容器的Service方法,Servlet容器拿到请求后,根据请求的URL和Servlet的映射关系,找到相应的Servlet,如果Servlet还没有被加载,就用反射机制创建这个Servlet,并调用Servlet的ini方法来完成初始化,接着调用Servlet的service方法来处理请求,把ServletResponse对象返回给HTTP服务器,HTTP服务器会把响应发送给客户端。
2 Tomcat中JVM配置优化
2.1 JVM内存模型图
方法区:JDK8中移动至元空间(解决了永久代OOM),主要存放类信息、常量池、方法数据、方法diamante、符号引用等。
栈:为每一个新线程执行分配的空间
程序计数器:通过程序计数器来记录某个线程的字节码执行位置,每个线程都有自己的独立计数器。
堆:分为老年代和新生代,新生代分为Eden和两个Servivor区,这两个区存在的必要是防止内存碎片化严重,如果严重的话,可以触发GC,
2.2 JVM配置选项
2.2.1 参数配置
Linux
JAVA_OPTS="
-server #服务器模式
-Xmx2048m #JVM最大允许分配的堆内存,按需分配
-Xms1024m #JVM初始分配的堆内存,一般和Xmx配置成一样以避免每次gc后JVM重新分配内存。
-Xmn256m #年轻代内存大小,整个JVM内存=年轻代 + 年老代,年轻代建议整个堆的3/8
-Xss512k #设置每个线程的堆栈大小
-XX:MetasoaceSize=256m #元空间内存初始大小,JDK8之前-XX:PermSize
-XX:MaxMetaspaceSize=512m ## 元空间内存最大大小
-XX:SurvivorRatio=8 # Eden区和幸存区比例大小,8表示Eden是suvivor区的8倍。
-XX:+DisableExplicitGC #忽略手动调用GC, System.gc()的调用就会变成一个空调用,完全不触发GC
-XX:+UseConcMarkSweepGC #并发标记清除(CMS)收集器
-XX:+CMSParallelRemarkEnabled #降低标记停顿
-XX:LargePageSizeInBytes=128m #内存页的大小
-XX:+UseFastAccessorMethods #原始类型的快速优化
-XX:+UseCMSInitiatingOccupancyOnly #使用手动定义初始化定义开始CMS收集
-XX:CMSInitiatingOccupancyFraction=70 #使用cms作为垃圾回收使用70%后开始CMS收集
-Ddruid.registerToSysProperty=true
"
2.2.2 垃圾回收器选择
参考原则:
- 程序数据量较小,选择串行收集器
- 应用运行在单核处理器上切没有暂停时间要求,可交由JVM自行选择或者选择串行收集器
- 如果考虑应用程序的峰值性能,没有考虑暂停时间,可以选择并行收集器
- 如果应用响应时间比整体吞吐量更重要,可以选择并发收集器。
垃圾回收器介绍
由上图可以看到jdk8默认垃圾收集器为:PS MarkSweep 和 PS Scavenge。 也就是说Java8的默认并不是G1。
这是”吞吐量优先“,注重吞吐量以及CPU资源敏感的场合都可以优先考虑Parallel Scavenge和Parallel Old(PS Mark Sweep)。Java8 默认就是这个。
下表为垃圾回收器的分类:
种类 | 描述 |
---|---|
串行收集器(Serial Collector) | 采用单线程执行所有的垃圾回收工作,适用于单核cpu服务器,无法利用多核硬件的优势 |
并行收集器(Parallel Collector) | 又名吞吐量收集器,以并行的方式执行年轻代的垃圾回收,该方式可以显著降低垃圾回收的开销。适用于多处理器或多线程硬件上运行的数据量较大的应用 |
并发收集器(Concurrent Collector) | 以并发的方式执行大部分垃圾回收工作,以缩短垃圾回收的暂停时间,适用于那些响应时间优先于吞吐量的应用,因为该收集器虽然最小化了暂停时间,但是会降低应用程序的性能 |
CMS收集器(Concurrent Mark Sweep Collector) | 并发标记清除收集器,适用于那些更愿意缩短垃圾回收暂停时间并且负担起与垃圾回收共享处理器资源的应用 |
G1收集器(Garbage-First Garbage Collector) | 使用于大容量内存的多核服务器,可以再满足垃圾回收暂停时间目标的同时,以最大性实现高吞吐量(JDK1.8之后) |
修改垃圾回收器
GC参数
参数 | 描述 |
---|---|
-XX:+UseSerialGC | 使用串行收集器 |
-XX:+UseParallelGC | 启用并行垃圾收集器,配置了该选项,那么-XX:+UseParalleloldGC默认启用 |
-XX:+UseParallelOldGC | FullGC采用并行收集,默认禁用,若果设置了-XX:+UseParallelGC则会启用 |
-XX:UseParNewGC | 年轻代采用并行收集器,如果设置了-XX:+UseConcMarkSweepGC选项,自动启用 |
-XX:ParallelGCThreads | 年轻代及老年代垃圾回收使用的线程数,默认值愈来愈JVM使用的CPU个数 |
-XX:+UserConcMarkSweepGC | 对于来年代,启用CMS垃圾收集器,当并行收集器无法满足应用的延迟需求时,推荐使用CMS或G1,启用该选项,-XX:UseParNewGC自动启用 |
-XX:+UseG1GC | 启用G1收集器,G1是服务器类型ed收集器,用于多核,大内存的机器,它在保持高吞吐量的情况下,高概率满足GC暂停时间的目标 |
垃圾回收将GC信息打印出来
选项 | 描述 |
---|---|
-XX:+PrintGC | 打印每次GC的信息 |
-XX:+PrintGCApplicationConcurrentTime | 打印最后一次暂停之后所经过的时间,即响应并发执行的时间 |
-XX:+PrintGCApplicationStoppedTime | 打印GC时应用暂停的时间 |
-XX:+PrintGCDateStamps | 打印每次GC的时间戳 |
-XX:PrintGCDetails | 打印每次GC的详细信息 |
-XX:PrintGCTaskTimeStamps | 打印每个GC工作线程任务的时间戳 |
-XX:PrintGCTimeStamps | 打印每次的时间戳 |
连接器调优
参数 | 说明 |
---|---|
maxConnections | 做大连接数,当达到该值后,服务器接收但不会处理更多的请求,额外的请求将会阻塞直到连接数低于maxConnections,可以通过ulimit -a查看服务器限制,对于CPU要求高时,建议不要配置过大,如果cpu要求不高,建议配置在2000 |
maxThreads | 最大线程数,需要根据服务器的硬件情况,进行一个合理的设置 |
acceptCount | 最大排队等待数,一台tomcat最大处理数,是maxConnections+acceptCount |