tomcat学习随笔

6 篇文章 0 订阅
2 篇文章 0 订阅

tomcat根路径

tomcat根路径目录结构示意图

一、组成

tomcat结构示意图

tomcat结构debug示意图

Server

tomcat的实例,支持多个Service

Service

web服务,主要包含Connector(多个)和Container(一个)

Connector

连接器(支持多个)作用是协议(如http)通信,负责监听端口来接收消息请求,并传递给Container进行业务处理,再将结果响应会客户端。
过程:网络通信- 应用层协议解析-Request/Response与ServeletRequest/ServeletResponse转化

属性含义
protocol监听的协议,默认是HTTP/1.1(7.0:org.apache.coyote.http11.Http11Protocol,8.0:org.apache.coyote.http11.Http11NioProtocol)
port监听的端口号
minThread服务器启动时创建的处理请求的线程数
maxThread最大可以创建的处理请求的线程数
enableLookups为true表示可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名;否则不进行DNS查询,而是返回其ip地址
acceptCount当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理
connectionTimeout超时的时间数(以毫秒为单位)
redirectPort当前Connector不支持SSL,收到了一个SSL传输请求时重定向的端口号
SSLEnabled是否开启 sll 验证,在Https 访问时需要开启
URIEncodingURL字符编码

更多见源码:org.apache.catalina.connector.Connector

ProtocolHandler

协议处理器,将不同协议和通信方式组合封装。本身就是顶层接口。
举例过程:org.apache.coyote.http11.Http11NioProtocol --> org.apache.tomcat.util.net.AbstractEndpoint(start --> startAcceptorThreads) --> org.apache.coyote.AbstractProtocol

Endpoint

端点,负责socket的接收和发送。顶层抽象类是org.apache.tomcat.util.net.AbstractEndpoint。

Acceptor
AbstractEndpoint子类的内部类,继承AbstractEndpoint的抽象内部类Acceptor,具体逻辑实现不同。
作用就是用于监听 Socket 连接请求。

Handler
AbstractEndpoint子类的内部类,继承AbstractEndpoint的内部接口Handler,供具体ProtocolHandler的内部类包装。
作用就是与Processor交互。

SocketProcessor
AbstractEndpoint子类的内部类,实现Runnable,具体逻辑实现不同。
作用就是通过run方法执行handler与Processor交互。

Processor

处理器,负责构建(填充数据)Request和Response对象

Adaptor

适配器,负责Request/Response与ServeletRequest/ServeletResponse转化。
如org.apache.catalina.connector.CoyoteAdapter

Container

一个Service中仅有一个,负责业务处理。包含Engine、Host、Context、Wrapper
container子类结构

tomcat container子类结构示意图
Engine

引擎,用于处理连接的执行器。一个Service只能配置一个Engine

属性含义
name名称
defaultHost默认的Host虚拟机域名,如localhost

更多见源码:org.apache.catalina.core.StandardEngine

Host

虚拟机,基于域名匹配至指定虚拟机,类似于nginx 当中的server

属性含义
name域名,必须配置,如localhost,至少有一个Host的name与Engine的defaultHost一致
appBase应用的根路径,支持相对路径(相对于tomcat安装目录根目录),默认webapps
unpackWARs是否自动解压war,默认true
autoDeploy是否热部署war:替换到整个Context环境(即包含session之类),默认true

更多见源码:org.apache.catalina.core.StandardHost

Context

应用上下文,隔离各个web应用,一个Context对应一个WebappClassLoader。一个Host下支持配置多个Context

属性含义
docBase应用物理路径,支持相对路径(相对于Host的appBase),默认不配置
path应用上下文路径,默认不配置
reloadable是否热加载class:替换掉WebappClassLoader。正式环境不用,默认false

更多见源码:org.apache.catalina.core.StandardContext

Wrapper
Pipeline结构
org.apache.catalina.core.ContainerBase
	org.apache.catalina.core.StandardPipeline

源码过程
org.apache.tomcat.util.net.AprEndpoint.SocketProcessor#doRun
org.apache.coyote.AbstractProtocol.AbstractConnectionHandler#process
org.apache.coyote.http11.AbstractHttp11Processor#process
// this.adapter.service(this.request, this.response);
	org.apache.catalina.connector.CoyoteAdapter#service
	// this.connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
		org.apache.catalina.core.StandardEngineValve#invoke
		// host.getPipeline().getFirst().invoke(request, response);
			org.apache.catalina.core.StandardContextValve#invoke
			// wrapper.getPipeline().getFirst().invoke(request, response);
				org.apache.catalina.core.StandardWrapperValve#invoke
				// filterChain.doFilter(request.getRequest(), response.getResponse());
					org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
					// this.servlet.service(request, response);
简易流程
EngineValue --> HostValue --> ContextValue --> WrapperValue --> FilterChain --> Servelet

二、运行

org.apache.catalina.startup.Bootstrap#main

1、初始化
org.apache.catalina.startup.Bootstrap#load
org.apache.catalina.startup.Catalina#load

1.1、加载配置初始化
org.apache.tomcat.util.digester.Digester#parse
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanDocument
com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl#scanStartElement
com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser#emptyElement
org.apache.tomcat.util.digester.Digester#startElement
调用各种rule
org.apache.tomcat.util.digester.ObjectCreateRule#begin

realClassName
	org.apache.catalina.core.StandardServer
	org.apache.catalina.core.StandardService
	org.apache.catalina.core.StandardEngine
	org.apache.catalina.core.StandardHost

org.apache.catalina.startup.ConnectorCreateRule#begin

org.apache.catalina.connector.Connector
	# HTTP/1.1。其他协(见org.apache.catalina.connector.Connector#setProtocol)类似
	org.apache.coyote.http11.Http11AprProtocol
		org.apache.tomcat.util.net.AprEndpoint
		org.apache.coyote.http11.Http11AprProtocol.Http11ConnectionHandler(包装Processor

1.2、init方法初始化
org.apache.catalina.core.StandardServer#initInternal
org.apache.catalina.core.StandardService#initInternal
org.apache.catalina.core.StandardEngine#initInternal
org.apache.catalina.connector.Connector#initInternal
org.apache.catalina.mbeans.MBeanFactory#createStandardContext

创建org.apache.catalina.connector.CoyoteAdapter并设置到org.apache.coyote.ProtocolHandler(如org.apache.coyote.http11.Http11AprProtocol|--> org.apache.coyote.AbstractProtocol#init
	|--> org.apache.tomcat.util.net.AbstractEndpoint#init
 		|--> org.apache.tomcat.util.net.AbstractEndpoint#bind(有具体实现类实现,目的就是创建如socket的通信)

2、启动
org.apache.catalina.startup.Bootstrap#start
org.apache.catalina.startup.Catalina#start
org.apache.catalina.core.StandardServer#startInternal
org.apache.catalina.core.StandardService#startInternal
org.apache.catalina.core.StandardEngine#startInternal
org.apache.catalina.connector.Connector#startInternal

|--> org.apache.coyote.AbstractProtocol#start
	|--> org.apache.tomcat.util.net.AbstractEndpoint#start
		|--> 相应的Endpoint实现类的内部Poller类#init + start

tomcat类加载器
三个类加载器

tomcat类加载器debug示意图

破坏双亲委派

tomcat破坏双亲委派debug示意图

三、热部署

jsp

替换JasperLoader,重新加载类

org.apache.jasper.servlet.JspServletWrapper#service
// this.ctxt.compile();
	// 判断是否做修改,修改则重新生成java和class文件,并设置jspLoader加载器为null,reload标志为true
	org.apache.jasper.JspCompilationContext#compile
	// this.jspLoader = null;
	// this.jspCompiler.compile();
	// this.jsw.setReload(true);
		org.apache.jasper.compiler.Compiler#compile
			org.apache.jasper.compiler.JDTCompiler#generateClass
// servlet = this.getServlet();
	// 如果reload为true,则销毁旧servelet对象(是否卸载相关类取决于GC能够回收)、生成新的对象以及初始化
	org.apache.jasper.servlet.JspServletWrapper#getServlet
	// this.destroy();
	// servlet = (Servlet)instanceManager.newInstance(this.ctxt.getFQCN(), this.ctxt.getJspLoader());
	// servlet.init(this.config);

替换org.apache.catalina.loader.WebappClassLoader,重新加载类
热加载class

org.apache.catalina.core.ContainerBase#backgroundProcess
// loader.backgroundProcess();
	org.apache.catalina.loader.WebappLoader#backgroundProcess
	// this.reloadable && this.modified() 为true就就行reload,其中this.modified() 是遍历了当前应用所有资源是否更改
		org.apache.catalina.core.StandardContext#reload
		// this.stop();
		// this.start();

war

重新初始化Context上下文环境,就是重新启动一个应用(断点过程一直无法执行到)。
代码调试过程,修改war展开后的应用根目录,虽然会被监听到,但因为是目录跳过了后续流程(没有reload、deployed或host存在该应用名称的缓存),相当于不处理(除非删除应用根目录之后再添加) 对war包更新、新增 和 应用移除(war包展开目录)有效
热部署war

org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor#processChildren
	org.apache.catalina.core.StandardContext#backgroundProcess
		org.apache.catalina.core.ContainerBase#backgroundProcess
			org.apache.catalina.util.LifecycleSupport#fireLifecycleEvent
			// 通过listener来通知
				org.apache.catalina.startup.HostConfig#lifecycleEvent
				// this.check();
					// 判断资源是否更改,更改则同样执行org.apache.catalina.core.StandardContext#reload
					org.apache.catalina.startup.HostConfig#checkResources
					// 条件符合情况下,重新初始化context
					org.apache.catalina.startup.HostConfig#deployApps()
						org.apache.catalina.startup.HostConfig#deployDescriptor

参考 tomcat9调优3:Tomcat类加载机制及其热部署热加载原理剖析

四、其他

请求头数据

org.apache.coyote.http11.Http11InputBuffer.parseHeader方法负责从【流】中解析数据并设置【org.apache.coyote.Request的headers】属性中

1、获取请求头数据,字段名都是小写的,原理见tomcat:中【this.chr >= 65 && this.chr <= 90】这部分代码逻辑。说明:仅对字段名处理成全小写,字段的值不会处理。
2、在【this.headerData.headerValue = this.headers.addValue(this.byteBuffer.array(), this.headerData.start, pos - this.headerData.start);】和【this.headerData.headerValue.setBytes(this.byteBuffer.array(), this.headerData.start, this.headerData.lastSignificantChar - this.headerData.start);】这两处代码实现从流中提取请求头数据到this.headers

如何保持请求头字段名:

a、通用就是自己重写Http11InputBuffer的parseHeader(新建相同包名的Http11InputBuffer类,复制tomcat的Http11InputBuffer类内容,将parseHeader方法中【this.chr >= 65 && this.chr <= 90】这部分代码逻辑注释掉)。有可能会影响到其他代码(自己项目或其他依赖的)的风险
b、手动处理有请求头字段名大小写敏感的业务

1、取值:通过 request.getHeader(“字段名”) 直接获取值。说明getHeader方法是不区分字段名大小写的
2、填充:先获取请求头全部数据给一个变量,再将需要保持字段名大小写名称的字段,填充到变量中(通过方式1先获取值再设置)。这个并不会改变请求头原有数据
servlet获取请求头数据
手动填充

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值