这么详细的tomcat你值得拥有~

第一节 Tomcat 体系架构
什么是 Web 服务器?
Web 服务器的定义
其实并没有标准定义。一般认为, Web 服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,可以向浏览器等 Web 客户端提供文
档,也可以放置网站文件,让全世界浏览;可以放置数据文件,让全世界下载。。 WEB 服务器的介绍
Web 服务器的特点。
1 、服务器是一种被动程序:只有当 Internet 上运行其他计算机中的浏览器发出的请求时,服务器才会响应。
2 、服务器一般使用 HTTP (超文本传输协议)与客户机浏览器进行信息交流,这就是人们常把它们称为 HTTP 服务器的原因。
3 Web 服务器不仅能够存储信息,还能在用户通过 Web 浏览器提供的信息的基础上运行脚本和程序。
什么是 Tomcat
Tomcat 是一款开源轻量级 Web 应用服务器 , 是一款优秀的 Servlet 容器实现。
Servlet Server Applet )是 Java Servlet 的简称,称为小服务程序或服务连接器,用 Java 编写的 服务器 端程序,具有独立于平台和 协议 的特性,主要
功能在于交互式地浏览和生成数据,生成动态 Web 内容。
Servlet 严格来讲是指 Java 语言实现的一个接口,一般情况下我们说的 Servlet 是指任何实现了这个 Servlet 接口的类。
实例化并调用 init() 方法初始化该 Servlet ,一般 Servlet 只初始化一次 ( 只有一个对象 )
service() (根据请求方法不同调用 doGet() 或者 doPost() ,此外还有 doHead() doPut() doTrace() doDelete() doOptions() destroy())
Server 不再需要 Servlet 时(一般当 Server 关闭时), Server 调用 Servlet destroy() 方法。
典型的 Servlet 的处理流程
1. 第一个到达服务器的 HTTP 请求被委派到 Servlet 容器。
2.Servlet 容器在调用 service() 方法之前加载 Servlet
3. 然后 Servlet 容器处理由多个线程产生的多个请求,每个线程执行一个单一的 Servlet 实例的 service() 方法。 Tomcat 版本介绍
Servlet2.X :项目目录结构必须要有 WEB-INF web.xml 等文件夹和文件,在 web.xml 中配置 servlet,filter,listener ,以 web.xml java web 项目的统一
入口
servlet 3.x 规范:项目中可以不需要 WEB-INF web.xml 等文件夹和文件,在没有 web.xml 文件的情况下,通过注解实现 servlet filter, listener 的声
明,当使用注解时,容器自动进行扫描。
同时 Tomcat8.5 进行了大量的代码重构,对比与 7.0 的版本,也符合 Tomcat 未来的代码架构体系。但是 Tomcat 的核心和主体架构还是一直保持这样
的。
8.5 版本特点
支持 Servlet3.1
默认采用 NIO ,移除 BIO 支持 NIO2(AIO)
支持 HTTP/2 协议
默认采用异步日志处理
为什么要讲 8.5 的版本,首先这个版本比较新,因为太老的版本比如 6.0 的版本 Servlet 不支持 3 所以会导致部署 SpringBoot 等项目有问题,同时这个
版本是在 9.0 出现以后发布的一个中间版本,主体架构延续 8.0 ,同时又实现了部分 9.0 的新特性。
Tomcat 启动
Tomcat 下载地址 :
https://tomcat.apache.org/download-80.cgi 一般启动
startup.bat/sh
启动成功的日志信息如下: 启动成功可以访问到的 Tomcat 首页
http://localhost:8080/ 为什么有这个默认首页: Tomcat “买一送一”的思想。默认 Tomcat 启动后加载 webapps 中的项目
IDE 中启动
一个 Sprei 项目配置和打包 IDE 中启动成功后的只有项目的访问,没有首页的显示的 嵌入式启动
SpringBoot 中一个 main 方法嵌入式启动 Tomcat Debug 启动
在项目发布后,我们有时候需要对基于生产环境部署的应用进行调试,以解决在开发环境无法重现的 BUG 。这时我们就需要用到应用服务器的远程
调试功能,这个主要是基于 JDK 提供的 JPDA Java Platform Debugger Architecture,Java 平台调试体系结构)。不过一般情况下用不到,这里简单讲一讲。
使用 DeBug 启动可以对基于生产环境部署的应用进行调试,以解决在开发环境无法重现的 BUG
使用 IDEA 远程部署 tomcat 和调试
1. catalina.sh 文件中加入以下的配置
CATALINA_OPTS= "-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1099
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=192.168.19.200
-agentlib:jdwp=transport=dt_socket,address=15833,suspend=n,server=y" export CATALINA_OPTS
以上端口可以随意改动,但是必要的是后续的设置必须保持一致,并且务必保证端口没有被占用,这些设置的端口在防火墙中是开放
状态;
其中 1099 的是 tomcat 远程部署连接端口;
15833 是远程调试的端口;
192.168.19.200 是远程的服务器的 Ip
2. Linux 上启动 tomcat, 使用命令启动
./bin/catalina.sh run & debug 启动测试
连接:
[2017-12-2308:47:03,592] Artifact devframe- server :war exploded: Artifact isnot deployed. Press 'Deploy' to start deployment
[2017-12-2308:47:03,650] Artifact devframe- server :war exploded: Artifact is being deployed, please wait...
Connected toserver
Connected to the target VM, address: '192.168.19.200:15833', transport: 'socket'
[2017-12-2308:47:11,434] Artifact devframe- server :war exploded: Error during artifact deployment. See serverlogfor details.
文件传输:
[2017/12/2320:47] Uploading to 192.168.19.200 completed in less than a minute: 357 files transferred (8 Mbit/s)
这样就能够成功远程部署并且调试了。
使用的技巧: 容易出现的问题
如果远程没有连接上,两个端口被占用或者防火墙屏蔽。除了 JMX server 指定的监听端口号外, JMXserver 还会监听一到两个随机端口
号,这个如果防火墙关闭了的话就不用考虑,如果使用了防火墙,还需要查看它监听的端口。
账号的相应的读写权限一定要有; Tomcat 项目部署及目录结构
项目部署
隐式部署
直接丢文件夹、 war jar webapps 目录, tomcat 会根据文件夹名称自动生成虚拟路径,简单,但是需要重启 Tomcat 服务器,包括要修改端口和访
问路径的也需要重启。
显式部署
添加 context 元素
server.xml 中的 Host 加入一个 Context (指定路径和文件地址),例如:
/comet 这个虚拟路径映射到了 D:\work_tomcat\ref-comet 目录下 (war 会解压成文件 ) ,修改完 servler.xml 需要重启 tomcat 服务器。
创建 xml 文件
conf/Catalina/localhost 中创建 xml 文件,访问路径为文件名,例如:
localhost 目录下新建 demo.xml ,内容为:
不需要写 path ,虚拟目录就是文件名 demo path 默认为 /demo ,添加 demo.xml 不需要重启 tomcat 服务器。
三种方式比较: 隐式部署:可以很快部署 , 需要人手动移动 Web 应用到 webapps 下,在实际操作中不是很人性化
添加 context 元素 : 配置速度快 , 需要配置两个路径,如果 path 为空字符串,则为缺省配置 , 每次修改 server.xml 文件后都要重新启动 Tomcat 服务器,重
新部署 .
创建 xml 文件 : 服务器后台会自动部署,修改一次后台部署一次,不用重复启动 Tomcat 服务器 , 该方式显得更为智能化。
Bin 执行脚本目录
startup 文件,主要是检查 catalina.bat/sh 执行所需环境,并调用 catalina.bat 批处理文件。启动 tomcat
catalina 文件,真正启动 Tomcat 文件,可以在里面设置 jvm 参数。后面性能调优会重点讲
shutdown 文件,关闭 Tomcat
脚本 version.sh startup.sh shutdown.sh configtest.sh 都是对 catalina.sh 的包装,内容大同小异,差异在于功能介绍和调用 catalina.sh 时的参数不同。
Version :查看当前 tomcat 的版本号,
Configtest :校验 tomcat 配置文件 server.xml 的格式、内容等是否合法、正确。
Service :安装 tomcat 服务,可用 net start tomcat 启动
web.xml
Tomcat 中所有应用默认的部署描述文件,主要定义了基础的 Servlet MIME 映射 (mime-mapping 文件类型,其实就是 Tomcat 处理的文件类型 ), 如果
部署的应用中不包含 Web.xml ,那么 Tomcat 将使用此文件初始化部署描述,反之, Tomcat 会在启动时将默认描述与定义描述配置进行合并。
加载一些 tomcat 内置的 servlet
DefaultServlet 默认的 , 加载静态文件 html,js,jpg 等静态文件。
JspServlet 专门处理 jsp
context.xml
用于自定义所有 Web 应用均需要加载的 Context 配置,如果 Web 应用指定了自己的 context.xml ,那么该文件的配置将被覆盖。 context.xml server.xml 中配置 context 的区别
server.xml 是不可动态重加载的资源,服务器一旦启动了以后,要修改这个文件,就得重启服务器才能重新加载。而 context.xml 文件则不然, tomcat
服务器会定时去扫描这个文件。一旦发现文件被修改(时间戳改变了),就会自动重新加载这个文件,而不需要重启服务器。
catalina.policy
权限相关 Permission Tomcat 是跑在 jvm 上的,所以有些默认的权限
tomcat-users.xml
配置 Tomcat server manager 信息
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
logging.properties
设置 tomcat 日志
控制输出不输出内容到文件,不能阻止生成文件,阻止声文件可用注释掉 webapps 目录
存放 web 项目的目录,其中每个文件夹都是一个项目;如果这个目录下已经存在了目录,那么都是 tomcat 自带的。项目。其中 ROOT 是一个特殊的
项目,在地址栏中没有给出项目目录时,对应的就是 ROOT 项目。 http://localhost:8080/examples ,进入示例项目。其中 examples 就是项目名,即文件夹
的名字。
lib 目录
Tomcat 的类库,里面是一大堆 jar 文件。如果需要添加 Tomcat 依赖的 jar 文件,可以把它放到这个目录中,当然也可以把应用依赖的 jar 文件放到这
个目录中,这个目录中的 jar 所有项目都可以共享之,但这样你的应用放到其他 Tomcat 下时就不能再共享这个目录下的 Jar 包了,所以建议只把 Tomcat
需要的 Jar 包放到这个目录下;
work 目录
运行时生成的文件,最终运行的文件都在这里。通过 webapps 中的项目生成的!可以把这个目录下的内容删除,再次运行时会生再次生成 work 目录。
当客户端用户访问一个 JSP 文件时, Tomcat 会通过 JSP 生成 Java 文件,然后再编译 Java 文件生成 class 文件,生成的 java class 文件都会存放到这个目
录下。
temp 目录
存放 Tomcat 的临时文件,这个目录下的东西可以在停止 Tomcat 后删除!
logs 目录
这个目录中都是日志文件,记录了 Tomcat 启动和关闭的信息,如果启动 Tomcat 时有错误,那么异常也会记录在日志文件中
localhost-xxx.log Web 应用的内部程序日志,建议保留
catalina-xxx.log
控制台日志 host-manager.xxx.log
Tomcat 管理页面中的 host-manager 的操作日志,建议关闭
localhost_access_log_xxx.log 用户请求 Tomcat 的访问日志(这个文件在 conf/server.xml 里配置),建议关闭
Conf 配置文件目录
server.xml
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
connectionTimeout="20000"
redirectPort="8443" />
resourceName="UserDatabase"/>
unpackWARs="true" autoDeploy="true">
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
unpackWARs="true" autoDeploy="true">
prefix="hzg_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
server.xml 中日志的 patter 解释
有效的日志格式模式可以参见下面内容,如下字符串,其对应的信息由指定的响应内容取代:
a - 远程 IP 地址
A - 本地 IP 地址
b - 发送的字节数,不包括 HTTP 头,或 “ - ” 如果没有发送字节
B - 发送的字节数,不包括 HTTP
h - 远程主机名
H - 请求协议
l ( 小写的 L)- 远程逻辑从 identd 的用户名(总是返回 ' - '
m - 请求方法
p - 本地端口
q - 查询字符串(在前面加上一个 如果它存在,否则是一个空字符串
r - 第一行的要求
s - 响应的 HTTP 状态代码
S - 用户会话 ID
t - 日期和时间,在通用日志格式
u - 远程用户身份验证
U - 请求的 URL 路径
v - 本地服务器名
D - 处理请求的时间(以毫秒为单位) T - 处理请求的时间(以秒为单位)
I (大写的 i - 当前请求的线程名称 Tomcat 组件及架构 Server
Server 是最顶级的组件,它代表 Tomcat 的运行实例,它掌管着整个 Tomcat 的生死大权;
提供了监听器机制,用于在 Tomcat 整个生命周期中对不同时间进行处理
提供 Tomcat 容器全局的命名资源实现, JNDI
监听某个端口以接受 SHUTDOWN 命令,用于关闭 Tomcat
Service
一个概念,一个 Service 维护多个 Connector 和一个 Container
Connector 组件
链接器:监听转换 Socket 请求,将请求交给 Container 处理,支持不同协议以及不同的 I/O 方式
Container
表示能够执行客户端请求并返回响应的一类对象,其中有不同级别的容器: Engine Host Context Wrapper
Engine
整个 Servler 引擎,最高级的容器对象
Host
表示 Servlet 引擎中的虚拟机,主要与域名有关,一个服务器有多个域名是可以使用多个 Host Context
用于表示 ServletContext, 一个 ServletContext 表示一个独立的 Web 应用
Wrapper
用于表示 Web 应用中定义的 Servlet
Executor
Tomcat 组件间可以共享的线程池
Tomcat 的核心组件
解耦:网络协议与容器的解耦。
Connector 链接器封装了底层的网络请求 (Socket 请求及相应处理 ), 提供了统一的接口,使 Container 容器与具体的请求协议以及 I/O 方式解耦。
Connector Socket 输入转换成 Request 对象,交给 Container 容器进行处理,处理请求后, Container 通过 Connector 提供的 Response 对象将结果写
入输出流。
因为无论是 Request 对象还是 Response 对象都没有实现 Servlet 规范对应的接口, Container 会将它们进一步分装成 ServletRequest ServletResponse.
Tomcat 的链接器
AJP 主要是用于 Web 服务器与 Tomcat 服务器集成, AJP 采用二进制传输可读性文本,使用保持持久性的 TCP 链接,使得 AJP 占用更少的带宽,并且
链接开销要小得多,但是由于 AJP 采用持久化链接,因此有效的连接数较 HTTP 要更多。
HTTP2.0 目前市场不成熟,这个技术点后续我们的三期、四期如果市面上协议很普遍了会考虑加入。
对于 I/0 选择,要根据业务场景来定,一般高并发场景下, APR NIO2 的性能要优于 NIO BIO ,( linux 操作系统支持的 NIO2 由于是一个假的,并
没有真正实现 AIO ,所以一般 linux 上推荐使用 NIO ,如果是 APR 的话,需要安装 APR 库,而 Windows 上默认安装了),所以在 8.5 的版本中默认是 NIO 第二节 Tomcat 源码分析(从启动流程到请求处理)
Tomcat 8.5 下载地址
https://tomcat.apache.org/download-80.cgi
Tomcat 启动流程
Tomcat 源码目录
catalina 目录
catalina 包含所有的 Servlet 容器实现,以及涉及到安全、会话、集群、部署管理 Servlet 容器的各个方面,同时,它还包含了启动入口。
coyote 目录
coyote Tomcat 链接器框架的名称,是 Tomcat 服务器提供的客户端访问的外部接口,客户端通过 Coyote 与服务器建立链接、发送请求并接收响应。
El 目录,提供 java 表达式语言
Jasper 模块提供 JSP 引擎
Naming 模块提供 JNDI 的服务
Juli 提供服务器日志的服务
tomcat 提供外部调用的接口 api
Tomcat 启动流程分析
1. 启动流程解析:注意是标准的启动,也就是从 bin 目录下的启动文件中启动 Tomcat 我们可以看到这个流程非常的清晰,同时注意到, Tomcat 的启动非常的标准,除去 Boostrap Catalin ,我们可以对照一下 Server.xml 的配置文
件。 Server,service 等等这些组件都是一一对照,同时又有先后顺序。
基本的顺序是先 init 方法,然后再 start 方法。
2. 加入调试信息 () :注意是标准的启动,也就是从 bin 目录下的启动文件中启动 Tomcat 可以看到,在源码中加入调试的信息和流程图是一致的。
我们可以看到,除了 Bootstrap catalina 类,其他的 Server,service 等等之类的都只是一个接口,实现类均为 StandardXXX 类。
我们来看下 StandardServer 类, 问题来了,我们发现 StandardServer 类中没有 init 方法,只有一个类似于 init initInternal 方法,这个是为什么?
带着问题我们进入下面的内容。
分析 Tomcat 请求过程
解耦:网络协议与容器的解耦。 Connector 链接器封装了底层的网络请求 (Socket 请求及相应处理 ), 提供了统一的接口,使 Container 容器与具体的请求协议以及 I/O 方式解耦。
Connector Socket 输入转换成 Request 对象,交给 Container 容器进行处理,处理请求后, Container 通过 Connector 提供的 Response 对象将结果写
入输出流。
因为无论是 Request 对象还是 Response 对象都没有实现 Servlet 规范对应的接口, Container 会将它们进一步分装成 ServletRequest ServletResponse.
问题来了,在 Engine 容器中,有四个级别的容器,他们的标准实现分别是 StandardEngine StandardHost StandardContext StandardWrapper
组件的生命周期管理
各种组件如何统一管理
Tomcat 的架构设计是清晰的、模块化、它拥有很多组件,加入在启动 Tomcat 时一个一个组件启动,很容易遗漏组件,同时还会对后面的动态组件拓
展带来麻烦。如果采用我们传统的方式的话,组件在启动过程中如果发生异常,会很难管理,比如你的下一个组件调用了 start 方法,但是如果它的上级
组件还没有 start 甚至还没有 init 的话, Tomcat 的启动会非常难管理,因此, Tomcat 的设计者提出一个解决方案:用 Lifecycle 管理启动,停止、关闭。
生命周期统一接口—— Lifecycle
Tomcat 内部架构中各个核心组件有包含与被包含关系,例如: Server 包含了 Service.Service 又包含了 Container Connector, 这个结构有一点像数据结
构中的树,树的根结点没有父节点,其他节点有且仅有一个父节点,每一个父节点有 0 至多个子节点。所以,我们可以通过父容器启动它的子容器,这
样只要启动根容器,就可以把其他所有的容器都启动,从而达到了统一的启动,停止、关闭的效果。
所有所有组件有一个统一的接口—— Lifecycle, 把所有的启动、停止、关闭、生命周期相关的方法都组织到一起,就可以很方便管理 Tomcat 各个容器
组件的生命周期。
Lifecycle 其实就是定义了一些状态常量和几个方法,主要方法是 init,start,stop 三个方法。
例如: Tomcat Server 组件的 init 负责遍历调用其包含所有的 Service 组件的 init 方法。 注意: Server 只是一个接口,实现类为 StandardServer, 有意思的是, StandardServer 没有 init 方法, init 方法是在哪里,其实是在它的父类 LifecycleBase
中,这个类就是统一的生命周期管理。 所以 StandardServer 最终只会调用到 initInternal 方法,这个方法会初始化子容器 Service init 方法 为什么 LifecycleBase 这么玩,其实很多架构源码都是这么玩的,包括 JDK 的容器源码都是这么玩的,一个类,有一个接口,同时抽象一个抽象骨架类,
把通用的实现放在抽象骨架类中,这样设计就方便组件的管理,使用 LifecycleBase 骨架抽象类,在抽象方法中就可以进行统一的处理,具体的内容见下面。
抽象类 LifecycleBase 统一管理组件生命周期 具体实现类 StandardXXX 类调用 initInternal 方法实现具体的业务处理。 分析 Tomcat 请求过程
Host 设计的目的
Tomcat 诞生时,服务器资源很贵,所以一般一台服务器其实可以有多个域名映射,满了满足这种需求,比如,我的这台电脑,有一个 localhost 域名,
同时在我的 hosts 文件中配置两个域名,一个 www.a.com 一个 localhost
Context 设计的目的
container 从上一个组件 connector 手上接过解析好的内部 request ,根据 request 来进行一系列的逻辑操作,直到调用到请求的 servlet ,然后组装好 response
返回给 connecotr
先来看看 container 的分类吧:
Engine
Host
Context
Wrapper
它们各自的实现类分别是 StandardEngine, StandardHost, StandardContext, and StandardWrapper ,他们都在 tomcat org.apache.catalina.core 包下。
它们之间的关系,可以查看 tomcat server.xml 也能明白(根据节点父子关系),这么比喻吧:除了 Wrapper 最小,不能包含其他 container 外, Context
内可以有零或多个 Wrapper Host 可以拥有零或多个 Host Engine 可以有零到多个 Host
Standard container 都是直接继承抽象类: org.apache.catalina.core.ContainerBase Tomcat 处理一个 HTTP 请求的过程
用户点击网页内容,请求被发送到本机端口 8080 ,被在那里监听的 Coyote HTTP/1.1 Connector 获得。
Connector 把该请求交给它所在的 Service Engine 来处理,并等待 Engine 的回应。
Engine 获得请求 localhost/test/index.jsp ,匹配所有的虚拟主机 Host
Engine 匹配到名为 localhost Host (即使匹配不到也把请求交给该 Host 处理,因为该 Host 被定义为该 Engine 的默认主机),名为 localhost Host 获得
请求 /test/index.jsp ,匹配它所拥有的所有的 Context Host 匹配到路径为 /test Context (如果匹配不到就把该请求交给路径名为 “ ” Context 去处理)。
path=“/test” Context 获得请求 /index.jsp ,在它的 mapping table 中寻找出对应的 Servlet Context 匹配到 URL PATTERN *.jsp Servlet, 对应于 JspServlet
类。
构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用 JspServlet doGet ()或 doPost () . 执行业务逻辑、数据存储等程序。
Context 把执行完之后的 HttpServletResponse 对象返回给 Host Host HttpServletResponse 对象返回给 Engine
Engine HttpServletResponse 对象返回 Connector
Connector HttpServletResponse 对象返回给客户 Browser
管道模式
管道与阀门
在一个比较复杂的大型系统中,如果一个对象或数据流需要进行繁杂的逻辑处理,我们可以选择在一个大的组件中直接处理这些繁杂的逻辑处理,
这个方式虽然达到目的,但是拓展性和可重用性差。因为牵一发而动全身。
管道是就像一条管道把多个对象连接起来,整体看起来就像若干个阀门嵌套在管道中,而处理逻辑放在阀门上。
它的结构和实现是非常值得我们学习和借鉴的。
首先要了解的是每一种 container 都有一个自己的 StandardValve
上面四个 container 对应的四个是:
StandardEngineValve
StandardHostValve
StandardContextValve
StandardWrapperValve
Pipeline 就像一个工厂中的生产线,负责调配工人( valve )的位置, valve 则是生产线上负责不同操作的工人。
一个生产线的完成需要两步:
1 ,把原料运到工人边上
2 ,工人完成自己负责的部分 tomcat Pipeline 实现是这样的:
1 ,在生产线上的第一个工人拿到生产原料后,二话不说就人给下一个工人,下一个工人模仿第一个工人那样扔给下一个工人,直到最后一个工人,而最
后一个工人被安排为上面提过的 StandardValve ,他要完成的工作居然是把生产资料运给自己包含的 container Pipeline 上去。
2 ,四个 container 就相当于有四个生产线( Pipeline ),四个 Pipeline 都这么干,直到最后的 StandardWrapperValve 拿到资源开始调用 servlet 。完成后返回
来,一步一步的 valve 按照刚才丢生产原料是的顺序的倒序一次执行。如此才完成了 tomcat Pipeline 的机制。
手写管道模式实现
具体代码见工程代码,通过一个简单的 demo, 我们了解到了,在管道中连接一个或者多个阀门,每一个阀门负责一部分逻辑处理,数据按照规定的顺
序往下流。此种模式分解了逻辑处理任务,可方便对某个任务单元进行安装、拆卸,提高流程的可拓展性,可重用性,机动性,灵活性。
源码分析
CoyoteAdapter service 方法里,由下面这一句就进入 Container 的。
connector.getContainer().getPipeline().getFirst().invoke(request, response);
是的,这就是进入 container 迷宫的大门,欢迎来到 Container 一个 StandardValve
来自 org.apache.catalina.core.StandardEngineValve invoke 方法: 其他的类似 StandardHostValve StandardContextValve StandardWrapperValve Tomcat 中定制阀门
管道机制给我们带来了更好的拓展性,例如,你要添加一个额外的逻辑处理阀门是很容易的。
1. 自定义个阀门 PrintIPValve ,只要继承 ValveBase 并重写 invoke 方法即可。注意在 invoke 方法中一定要执行调用下一个阀门的操作,否则会出现
异常。
public class PrintIPValve extends ValveBase{
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
System.out.println("------ 自定义阀门 PrintIPValve:"+request.getRemoteAddr());
getNext().invoke(request,response);
}
}
2. 配置 Tomcat 的核心配置文件 server.xml, 这里把阀门配置到 Engine 容器下,作用范围就是整个引擎,也可以根据作用范围配置在 Host 或者是 Context
< Valve className ="org.apache.catalina.valves.PrintIPValve" />
3. 源码中是直接可以有效果,但是如果是运行版本,则可以将这个类导出成一个 Jar 包放入 Tomcat/lib 目录下,也可以直接将 .class 文件打包进
catalina.jar 包中。
Tomcat 中提供常用的阀门
AccessLogValve ,请求访问日志阀门,通过此阀门可以记录所有客户端的访问日志,包括远程主机 IP ,远程主机名,请求方法,请求协议,会话 ID
请求时间,处理时长,数据包大小等。它提供任意参数化的配置,可以通过任意组合来定制访问日志的格式。
JDBCAccessLogValve ,同样是记录访问日志的阀门,但是它有助于将访问日志通过 JDBC 持久化到数据库中。
ErrorReportValve ,这是一个讲错误以 HTML 格式输出的阀门
PersistentValve ,这是对每一个请求的会话实现持久化的阀门 RemoteAddrValve ,访问控制阀门。可以通过配置决定哪些 IP 可以访问 WEB 应用
RemoteHostValve ,访问控制阀门,通过配置觉得哪些主机名可以访问 WEB 应用
RemoteIpValve ,针对代理或者负载均衡处理的一个阀门,一般经过代理或者负载均衡转发的请求都将自己的 IP 添加到请求头” X-Forwarded-For ”中,
此时,通过阀门可以获取访问者真实的 IP
SemaphoreValve ,这个是一个控制容器并发访问的阀门,可以作用在不同容器上。
第三节 Tomcat 源码分析(类加载与类加载器)
Tomcat 的挑战
Tomcat 上可以部署多个项目
Tomcat 的一般部署,可以通过多种方式启动一个 Tomcat 部署多个项目,那么 Tomcat 在设计时会遇到什么挑战呢? Tomcat 运行时需要加载哪些类
Tomcat 中的多个项目可能存在相同的类
Tomcat 中类加载的挑战
源码分析彻底弄懂 Tomcat 的类加载
类加载与类加载器
类加载
类加载: 主要是将 .class 文件中的二进制字节读入到 JVM
我们可以看到因为这个定义,所以并没有规定一定是要磁盘加载文件,可以通过网络,内存什么的加载。只要是二进制流字节数据, JVM 就认。
类加载过程:
1. 通过类的全限定名获取该类的二进制字节流;
2. 将字节流所代表的静态结构转化为方法区的运行时数据结构
3. 在内存中生成一个该类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口 类加载器
定义: JVM 设计者把 类加载 这个动作放到 java 虚拟机外部去实现,以便让应用程序决定如何获取所需要的类。实现这个动作的代码模块成为 类加
载器
类与类加载器
对于任何一个类,都需要由加载它的类加载器和这个类来确定其在 JVM 中的唯一性。也就是说,两个类来源于同一个 Class 文件,并且被同一个类加
载器加载,这两个类才相等。
注意:这里所谓的“相等”,一般使用 instanceof 关键字做判断。
类加载器与双亲委派模型
类加载器
启动类加载器:该加载器使用 C++ 语言实现,属于虚拟机自身的一部分。
启动类加载器( Bootstrap ClassLoader : 负责加载 JAVA_HOME\lib 目录中并且能被虚拟机识别的类库加载到 JVM 内存中,如果名称不符合的类库即使
lib 目录中也不会被加载。该类加载器无法被 java 程序直接引用
拓展类加载器与应用程序类加载器:另一部分就是所有其它的类加载器,这些类加载器是由 Java 语言实现,独立于 JVM 外部,并且全部继承抽象类
java.lang.ClassLoader
扩展类加载器 (Extension ClassLoader): 该加载器主要负责加载 JAVA_HOME\lib\ext 目录中的类库,开发者可以使用扩展加载器。
应用程序类加载器( Application ClassLoader : 该列加载器也称为系统加载器,它负责加载用户类路径 (Classpath) 上所指定的类库,开发者可以直接使
用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器 双亲委派模型
定义: 双亲委派模型的工作过程为:如果一个类加载器收到了类请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父加载器去完成,
每一层都是如此,因此所有类加载的请求都会传到启动类加载器,只有当父加载器无法完成该请求时,子加载器才去自己加载。
实现方式: 该模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。子类加载器不是以继承的关系来实现,而是通过
组合关系来复用父加载器的代码。
意义: 好处双亲委派模型的好处就是 java 类随着它的类加载器一起具备了一种带有优先级的层次关系。例如: Object, 无论那个类加载器去加载该类,
最终都是由启动类加载器进行加载的,因此 Object 类在程序的各种类加载环境中都是一个类。如果不用改模型,那么 java.lang.Object 类存放在 classpath
中,那么系统中就会出现多个 Object 类,程序变得很混乱。
双亲委派模型
从虚拟机的角度来说,有两种不同的类加载器:一种是启动类加载器( Bootstrap ClassLoader , 该加载器使用 C++ 语言实现,属于虚拟机自身的一
部分。另一部分就是所有其它的类加载器,这些类加载器是由 Java 语言实现,独立于 JVM 外部,并且全部继承抽象类 java.lang.ClassLoader.
java 开发人员的角度看,大部分 java 程序会用到以下三种系统提供的类加载器:
1 、启动类加载器( Bootstrap ClassLoader : 负责加载 JAVA_HOME\lib 目录中并且能被虚拟机识别的类库加载到 JVM 内存中,如果名称不符合的类
库即使在 lib 目录中也不会被加载。该类加载器无法被 java 程序直接引用。
2 、扩展类加载器 (Extension ClassLoader): 该加载器主要负责加载 JAVA_HOME\lib\ext 目录中的类库,开发者可以使用扩展加载器。
3 、应用程序类加载器( Application ClassLoader : 该列加载器也称为系统加载器,它负责加载用户类路径 (Classpath) 上所指定的类库,开发者可以
直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
JVM 中的类加载器源码分析
AppClassLoader URLClassLoader SecureClassLoader Tomcat 中的类加载解决方案
Tomcat 类加载的考虑
隔离性
Web 应用类库相互隔离,避免依赖库或者应用包相互影响,比如有两个 Web 应用,一个采用了 Spring 4, 一个采用了 Spring 5, 而如果如果采用同一个
类加载器,那么 Web 应用将会由于 jar 包覆盖而无法启动成功。 灵活性
因为隔离性,所以 Web 应用之间的类加载器相互独立,如果一个 Web 应用重新部署时,该应用的类加载器重新加载,同时不会影响其他 web 应用。
比如:不需要重启 Tomcat 的创建 xml 文件的类加载,
还有 context 元素中的 reloadable 字段:如果设置为 true 的话, Tomcat 将检测该项目是否变更,当检测到变更时,自动重新加载 Web 应用。
性能
由于每一个 Web 应用都有一个类加载器,所以在 Web 应用在加载类时,不会搜索其他 Web 应用包含的 Jar 包,性能自然高于只有一个类加载的情况。 Tomcat 中的类加载器
Tomcat 提供 3 个基础类加载器( common catalina shared )和 Web 应用类加载器。 Tomcat 中的类加载源码分析
三个基础类加载器
介绍
3 个基础类加载器的加载路径在 catalina.properties 配置,默认情况下, 3 个基础类加载器的实例都是一个。
createClassLoader 调用 ClassLoaderFactory 属于一种工厂模式,并且都是使用 URLClassLoader 默认情况三个是一个实例,但是可以通过修改配置创建 3 个不同的类加载机制,使它们各司其职。
举个例子:如果我们不想实现自己的会话存储方案,并且这个方案依赖了一些第三方包,我们不希望这些包对 Web 应用可见,因此我们可以配置
server.loader, 创建独立的 Catalina 类加载器。
共享性:
Tomcat 通过 Common 类加载器实现了 Jar 包在应用服务器与 Web 应用之间的共享,
通过 Shared 类加载器实现了 Jar 包在 Web 应用之间的共享
通过 Catalina 类加载器加载服务器依赖的类。 类加载工厂
因为类加载需要做很多事情,比如读取字节数组、验证、解析、初始化等。而 Java 提供的 URLClassLoader 类能够方便的将 Jar Class 或者网络资源加
载到内存中。而 Tomcat 中则用一个工厂类, ClassLoaderFactory 把创建类加载器的细节进行封装,可以通过它方便的创建自定义类加载器。 使用加载器工厂的好处
1. ClassLoadFactory 有一个内部 Repository ,它就是表示资源的类,资源的类型用一个 RepositoryType 的枚举表示。
2. 同时我们看到,如果在检查 jar 包的时候,如果有检查的 URL 地址的如果检查有异常就忽略掉,可以确保部分类加载正确。 尽早设置线程上下文类加载器
每一个运行线程中有一个成员 ContextClassLoader, 用于在运行时动态载入其他类,当程序中没有显示声明由哪个类加载器去加载哪个类(比如 new 出一
个类时),将默认由当前线程类加载器加载,所以一般系统默认的 ContextClassLoad 是系统类加载器。
一般在实际的系统上,使用线程上下文类加载器,可以设置不同的加载方式,这个也是 Java 灵活的类加载方式的体现,也可以很轻松的打破双亲委派
模式,同时也会避免类加载的异常。
Webapp 类加载器
每个 web 应用会对一个 Context 节点,在 JVM 中就会对应一个 org.apache.catalina.core.StandardContext 对象,而每一个 StandardContext 对象内部都
一个加载器实例 loader 实例变量。这个 loader 实际上是 WebappLoader 对象。而每一个 WebappLoader 对象内部关联了一个 classLoader 变量(就这这个
类的定义中,可以看到该变量的类型是 org.apache.catalina.loader.WebappClassLoader )。
所以,这里一个 web 应用会对应一个 StandardContext 一个 WebappLoader 一个 WebappClassLoader 一个 web 应用对应着一个 StandardContext 实例,每个 web 应用都拥有独立 web 应用类加载器 (WebappClassLoader) ,这个类加载器在
StandardContext.startInternal() 中被构造了出来。 注意这里:设置加载器和获取加载器都使用了读写锁机制,确保多线程情况下对共享资源的访问不会出现问题。
同时因为 Tomcat 的生命周期管理,必定会调用 WebappLoader.java startInternal() 方法,该方法中 new 出了 所以总结一句话,如果没有弄懂 Tomcat 的启动流程,以及弄懂 Tomcat 的生命周期的管理,很多地方的源码是没有办法没有看懂,所以看源码也有一
个先后顺序。 热加载源码分析
当配置信息中有 reloadable 的属性,并且值为 true 时, Tomcat 怎么去完成这个过程呢?
还是看源码,据 Tomcat 的启动流程,我们分析下 Context 的初始化 start 方法,根据之前的课程我们可知道, Context 只是一个接口,具体实现类是
StandardContext ,我们分析下 startInternal 方法(此方法由之前的抽闲骨架类中的 start 方法触发) 我发现有一个线程启动的方法 threadStart(), 由上图我们知道,这个是父类中调用, JasperLoader 第四节 Tomcat 性能优化
嵌入式 Tomcat
为什么要嵌入式
为什么需要嵌入式启动,我们由之前一般的 Tomcat 启动可知, Tomcat 组件非常多,启动流程步骤比较多,但是往往我们一般就只需要简单快速的部
署一个 Web 项目,所以简单实用的嵌入式 Tomcat 就诞生而来。
部署复杂度
如果按照传统部署,我们需要下载 Tomcat, 同时需要配置服务器,同时还需要修改端口,同时也要避免应用系统的 jar 包与服务器中存在的 lib 包的冲
突,所有的这些都会增加部署的复杂度,并且这种配置大部分还是一次性的,不可重用。如果你遇到大规模的服务器集群环境(部署 N 多个应用)时,
会增加我们的运维成本,如果按照嵌入式启动,这种方式几乎是一键式的,可以把以上问题轻松的解决。
架构约束
Tomcat 启动的时候默认不单单只启动了 HTTP 协议,还有 AJP 协议等等,如果我们就只想简简单单的启动一个 HTTP 服务,同时不想启动那些多组件,
可以使用嵌入式 Tomcat ,避免在部署启动时的架构约束。
微服务架构
现在微服务已经是主流的架构,其中微服务中每项服务都拥有自己的进程并利用轻量化机制实现通讯。这些服务都是围绕业务功能建立,可以自动
化部署或独立部署。将微服务架构与 Tomcat 技术相结合,可以轻松将系统部署到云服务器上。当前 SpringBoot 支持的嵌入式服务器组件就是 Tomcat. 嵌入式启动实战
启动 Servlet 通过实战可知,我们添加了一个 HttpServlet 提供对外服务,这样我们的 Tomcat 就可以简单的提供一个 Servlet 服务,而不提供页面访问( JSP HTML
等),可以非常灵活的控制 Servlet 的加载以及请求链接的分配,而不受限制于 Context 与应用的一一对应关系。
嵌入式启动应用
这个项目工程是一个服务器推送的工程,包含了 JSP 修改端口为 80 看源码可知, Tomcat 通过一个内部类 DefaultWebXmlListener 用来加载 Web 应用的配置。 嵌入式常用的配置
setConnector: 用于设置链接器,包括协议、 I/0 、端口、压缩、加密等等。
setHost: 用于设置 Host
setBaseDir:: 用于设置临时文件的目录,这些目录用于存放 JSP 生成的源代码及 Class 文件
SpringBoot 中使用嵌入式 Tomcat
没有用过 SpringBoot 的同学可以不看。 Tomcat 性能优化
server.xml 优化
其实网络的 Tomcat 配置信息的文章很多,五花八门, King 老师一般推荐使用 Tomcat 自身提供的配置帮助文档,因为你只要下载了 Tomcat, 并且启动
了它,那么 Tomcat 就会提供最官方,最准确的官方参数说明文档。
下载 Tomcat 后不要删掉默认的程序包 .
一般 Tomcat 启动后,不改动端口的话,默认是 8080, 我们输入 localhost:8080 访问下。 这个会出现一个英文的 Tomcat 环境界面,包括各种文档说明信息都在此,我推荐使用谷歌浏览器,因为这个浏览器自带翻译功能。 Connector 连接器 IO 模型优化策略
连接器模式改为 NIO 模式 ,NIO 模式最大化压榨了 CPU ,把时间片更好利用起来 ,NIO 适合大量长连接。 最大线程优化策略
maxThreads 属性设置为简单 200. 这对于单个核心计算机来说很好,但是可以根据生产计算机上的处理器数量 线性 增加。在具有四个处理器的计算机
上,将此值设置为 800 1000 之间的任何值都不会导致问题。如果配置的数量最终远远超过所需的线程数,则当服务器负载较低时,线程池将自然缩减
此数字。 压缩 gzip 连接器传输
客户端和服务器之间的任何主要是文本的通信,无论是 HTML XML 还是简单的 Unicode ,都可以使用简单的标准 GZIP 算法定期压缩高达 90 %。这可
以对减少网络流量产生巨大影响,允许响应更快地发送回客户端,同时允许更多网络带宽可用于其他网络繁重的应用程序。
```xml
connectionTimeout="20000"
redirectPort="8443" executor="tomcatThreadPool" URIEncoding="utf-8" compression="on"
compressionMinSize="50" noCompressionUserAgents="gozilla, traviata"
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" /> 配置线程池 Executor
name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="150"
minSpareThreads="4" />
executor="tomcatThreadPool"
port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
minProcessors="5"
maxProcessors="75"
acceptCount="1000" /> Executor 的属性 去除 value 访问 tomcat 记录
```xml
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
```
关闭自动重载,热部署方式
> 关闭自动重载,默认是 true( 不同版本中有差异 ), 自动加载增加运行开销并且很容易内存溢出 web.xml 优化
去掉不必要的 servlet
如当前应用是 REST 应用 ( 微服务 )
. 去掉不必要的资源: JspServlet
. seesion 也可以移除 JspServlet 优化
. checkInterval - 如果 “development” 属性为 false “checkInterval” 大于 0 ,则使用后台编译。 “checkInterval” 是查看 JSP 页面 ( 包括其附属文件 )
是否需要重新编译的两次检查时间间隔(单位:秒)。缺省值为 0 秒。
. classdebuginfo - 类文件在编译时是否显示调试 (debugging) 信息? true false ,缺省为 true
. classpath - 编译 servlet 时要使用的类路径,当 ServletContext 属性 org.apache.jasper.Constants.SERVLET_CLASSPATH 未设置的情况下,
该参数才有效。在 Tomcat 中使用到 Jasper 时,该属性总被设置。缺省情况下,该路径基于你当前的 web 应用动态生成。
. compiler – Ant 将要使用的 JSP 页面编译器,请查阅 Ant 文档获取更多信息。如果该参数未设置,那么默认的 Eclipse JDT Java 编译器将被用
来代替 Ant 。没有缺省值。
. compilerSourceVM - 编译源文件时采用哪一个 JDK 版本? ( 缺省为 JDK 1.4)
. compilerTargetVM - 运行类文件时采用哪一个 JDK 版本? ( 缺省为 JDK 1.4)
. development - 是否让 Jasper 用于开发模式?如果是,检查 JSPs 修改的频率,将通过设置 modificationTestInterval 参数来完成。
true false
缺省为 true
. displaySourceFragment - 异常信息中是否包含出错的源代码片段? true false ,缺省为 true
. dumpSmap - JSR45 调试的 SMAP 信息是否转存到文件? true false ,缺省为 false 。当 suppressSmap true 时,该参数为 false
. enablePooling - 确定是否共享标签处理器, true false ,缺省为 true
. engineOptionsClass - 允许指定的类来配置 Jasper 。如果没有指定,则使用默认的 Servlet 内置参数 (EmbeddedServletOptions)
. errorOnUseBeanInvalidClassAttribute - 在一个 useBean action 中,当类属性的值不是一个合法的 bean class 时, Jasper 是否抛出异常? true
false ,缺省为 true
. fork - 是否让 Ant 派生出 JSP 页面多个编译,它们将运行在一个独立于 Tomcat JVM 上。 true 或者 false, 缺省为 true.
. enStringAsCharArray - 是否把字符串转换为字符数组?在某些情况下会改善性能。缺省为 false.
. eClassId - 当使用标签时,发送给 Internet Explorer class-id 的值。缺省为: 8AD9C840-044E-11D1-B3E9-00805F499D93
. javaEncoding - 生成 java 源文件时采用的 Java 文件编码。缺省为 UTF-8
. keepgenerated - 是否保存每个页面生成的 java 源代码,而不删除。 true false ,缺省为 true
. mappedfile - 是否对每个输入行都用一条 print 语句来生成静态内容,以方便调试。 true false ,缺省为 true . modificationTestInterval - 检查 JSP 页面修改的间隔时间(单位:秒),在间隔时间内, JSP 及其包含的页面将不会检查。当间隔时间为 0 时,
JSP 每一次访问都会被检查。仅仅适用于开发模式(参数 development true )。缺省为 4 秒。从 JSP 每次开始访问开始计时, N 秒以后检查,变化
就编译,每次访问都刷新开始时间,默认 4
. scratchdir - 当编译 JSP 页面时使用的 scratch 目录。缺省为当前 WEB 应用的工作目录。
. suppressSmap - 是否禁止 JSR45 调试时生成 SMAP 信息? true false ,缺省为 false
. trimSpaces - 是否去掉模板文本中行为和指令之间的空格。缺省为 false
· xpoweredBy - 确定生成的 Servlet 是否加上 X-Powered-By 响应头? true false ,缺省为 false Tomcat 常见问题解决
Tomcat 版本升级 Tomcat 常见面试题
1.Tomcat 有哪几种部署方式?
隐式部署
直接丢文件夹、 war jar webapps 目录, tomcat 会根据文件夹名称自动生成虚拟路径,简单,但是需要重启 Tomcat 服务器,包括要修改端口和访问路径的也需要重启。
显式部署
添加 context 元素
server.xml 中的 Host 加入一个 Context (指定路径和文件地址),例如:
/comet 这个虚拟路径映射到了 D:\work_tomcat\ref-comet 目录下 (war 会解压成文件 ) ,修改完 servler.xml 需要重启 tomcat 服务器。
创建 xml 文件
conf/Catalina/localhost 中创建 xml 文件,访问路径为文件名,例如:
localhost 目录下新建 demo.xml ,内容为:
不需要写 path ,虚拟目录就是文件名 demo path 默认为 /demo ,添加 demo.xml 不需要重启 tomcat 服务器。
三种方式比较:
隐式部署:可以很快部署 , 需要人手动移动 Web 应用到 webapps 下,在实际操作中不是很人性化
添加 context 元素 : 配置速度快 , 需要配置两个路径,如果 path 为空字符串,则为缺省配置 , 每次修改 server.xml 文件后都要重新启动 Tomcat 服务器,重新部署 . 创建 xml 文件 : 服务器后台会自动部署,修改一次后台部署一次,不用重复启动 Tomcat 服务器 , 该方式显得更为智能化。
2. Tomcat 核心组件是哪些?
Tomcat 的核心组件是链接器 (connector) 和容器 (Container),
链接器 (connector) 封装了底层的网络请求 (Socket 请求及相应处理 ), 提供了统一的接口 .
容器 (Container) 则专注处理 Servlet Tomcat 本质上就是 Servlet 容器。
3. 什么是 Tomcat Valve
在一个大的组件中直接处理这些繁杂的逻辑处理 , 使用管道( pipeline )可以把把多个对象连接起来,而 Valve( 阀门 ) 整体看起来就像若干个阀门嵌套在管道中,而处理逻辑放在
阀门上。
管道 (Pipeline) 就像一个工厂中的生产线,负责调配工人( valve )的位置, valve 则是生产线上负责不同操作的工人。
4. Tomcat 有哪几种 Connector 运行模式 ( 优化 )
1.
bio(blocking I/O) 同步阻塞 I/O tomcat8.5 版本已经移除)
2.
nio(non-blocking I/O) 同步非阻塞 I/O
3.
Nio2/AIO
异步非阻塞 I/0
4.
apr(Apache Portable Runtime/Apache 可移植运行库 )
对于 I/0 选择,要根据业务场景来定,一般高并发场景下, APR NIO2 的性能要优于 NIO BIO ,( linux 操作系统支持的 NIO2 由于是一个假的,并没有真正实现 AIO ,所以
一般 linux 上推荐使用 NIO ,如果是 APR 的话,需要安装 APR 库,而 Windows 上默认安装了),所以在 8.5 的版本中默认是 NIO 5. tomcat 容器是如何创建 servlet 类实例?用到了什么原理?
当容器启动时,会读取在 webapps 目录下所有的 web 应用中的 web.xml 文件,然后对 xml 文件进行解析,并读取 servlet 注册信息。然后,将每个应用中注册的 servlet 类都进
行加载,并通过反射的方式实例化(也有可能是在第一次请求时实例化)。
6. tomcat 如何优化?
7. tomcat JVM 如何调优
一般我们优化启动时的堆内存设置 ,Windows , 在文件 {tomcat_home}/bin/catalina.bat Unix 下,在文件 $CATALINA_HOME/bin/catalina.sh 的前面,增加如下设置:
JAVA_OPTS= ”‘ $JAVA_OPTS -Xms[ 初始化内存大小 ] -Xmx[ 可以使用的最大内存 ]
一般说来,你应该使用物理内存的 80% 作为堆大小。建议设置为 70 %;建议设置 [[ 初始化内存大小 ] 等于 [ 可以使用的最大内存 ] ,这样可以减少平凡分配堆而降低性能。
8. 说出 Tomcat 中用到的任意一种设计模式?
9. Tomcat 中类加载的顺序
当应用需要到某个类时,则会按照下面的顺序进行类加载:
1 使用 bootstrap 引导类加载器加载
2 使用 system 系统类加载器加载
3 使用应用类加载器在 WEB-INF/classes 中加载 4 使用应用类加载器在 WEB-INF/lib 中加载
5 使用 common 类加载器在 CATALINA_HOME/lib 中加载
10.
什么是双亲委派模型?
定义: 双亲委派模型的工作过程为:如果一个类加载器收到了类请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父加载器去完成,每一层都是如此,因此所
有类加载的请求都会传到启动类加载器,只有当父加载器无法完成该请求时,子加载器才去自己加载。
实现方式: 该模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。子类加载器不是以继承的关系来实现,而是通过组合关系来复用父加载器的
代码。
意义: 好处双亲委派模型的好处就是 java 类随着它的类加载器一起具备了一种带有优先级的层次关系。例如: Object, 无论那个类加载器去加载该类,最终都是由启动类加载
器进行加载的,因此 Object 类在程序的各种类加载环境中都是一个类。如果不用改模型,那么 java.lang.Object 类存放在 classpath 中,那么系统中就会出现多个 Object 类,程序变
得很混乱。
11.
既然 Tomcat 不遵循双亲委派机制,那么如果我自己定义一个恶意的 HashMap ,会不会有风险呢?
不会有风险,如果有, Tomcat 都运行这么多年了,那群 Tomcat 大神能不改进吗? tomcat 不遵循双亲委派机制,只是自定义的 webAppclassLoader 不遵循,但上层的类加载
器还是遵守双亲委派的。

你可能感兴趣的:(tomcat,后端java开发)