有关SpringBoot启动Tomcat,添加过滤器及Servlet请点击此处相关部分。
有关SpringBoot开启Tomcat监听请点击此处相关部分。
类加载器
类加载器是用于加载Java类到Java虚拟机中的组件,它负责读取类的Class文件,并转换成java.lang.Class
的一个实例。
类加载的方式分为隐式加载和显示加载两种。隐式加载指的是程序在使用new 等方式创建对象时,会隐式地调用类的加载器把对应的类加载到JVM中(见ClassLoader->loadClass()
)。显式加载指的是通过直接调用Class.forName()
方法把所需的类加载到JVM中。
双亲委派机制指的是在加载类时先由父加载器加载,父加载器再由它的父加载器加载,层层向上,如果加载到就直接返回,加载不到再尝试由自己加载。
Tomcat的类加载架构图如下所示,Web环境的类加载器是WebAppClassLoader,它的加载顺序为先启动类、扩展类、应用程序类,再WebAPP类,最后Common类,因此它破坏了双亲委派机制。这么设计的主要原因,是为了解决同一个Web服务器里,1.各个Web项目各自使用的Java类库要互相隔离,2.各个项目可以提供共享的Java类库的问题。
生命周期与监听器
上篇说到,判断session是否过期在Context的backgroundProcess()方法执行,它是ContainerBase
类下的一个方法,四大组件都是它的子类,ContainerBase
类又是LifecycleMBeanBase
的子类,该类与生命周期有关。
接口Lifecycle
定义有哪些事件,枚举类LifecycleState
列出生命状态对应的事件类型,四大组件初始化的时候,会调用LifecycleBase->addLifecycleListener()
添加监听器,监听器是LifecycleListener
接口的实现类,当生命周期变化时,会调用fireLifecycleEvent()
,之后监听器处理对应的事件,可见监听器的设计模式是观察者模式。
LifecycleBase->fireLifecycleEvent():
// this是当前的组件,type是事件类型,lifecycleListeners是监听器集合
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
// 监听器处理事件,不同监听器处理不同的事件类型
listener.lifecycleEvent(event);
}
剩余过程
以返回JSON为例,当消息状态置为CLIENT_FLUSH时,客户端已经看到数据了,服务端还需完成回收的工作。
- StandardWrapperValve
- invoke()
- filterChain.release() 释放过滤器链
- invoke()
- CoyoteAdapter
- service()
- response.finishResponse() 1.如果bb和cb还有剩余的话,写入剩余部分到SocketWrapperBase的socketBufferHandler的writeBuffer中 2.将消息状态置为CLOSE,调用钩子函数,将剩余内容输出到客户端
- request.recycle() 回收请求相关数据,这里的请求类是原始请求类的包装类,ByteBuffer、CharBuffer的回收只是下标和limit置为0,集合有关的置为空集合,对象有关的置为null
- response.recycle() 回收响应相关数据,处理同上
- service()
- Http11Processor
- service()
- inputBuffer.nextRequest() 回收请求相关数据,这里的请求类是与网络IO交互的最原始的请求类
- outputBuffer.nextRequest() 回收响应相关数据,处理同上
- while(...) 再一次尝试解析请求行,因为客户端没有发送数据了,所以跳出循环
- service()
- AbstractProtocol->ConnectionHandler
- process()
- connections.remove(socket) connections去除本次会话相关内容,一次请求是一个socket
- process()
能够看出数组类型并没有将数据清空,而是将起始和结束下标置为0,频繁销毁的对象也仅是部分属性清空,然后入栈,这些对于Tomcat性能的提升都有不小的帮助。
Tomcat处理过程总结
以下内容摘自《Tomcat内核剖析》。
- 当Tomcat启动后, Connector组件的接收器(Acceptor)将会监听是否有客户端套接字连接并接收Socket。
- 一旦监听到客户端连接,则将连接交由线程池Executor处理,开始执行请求响应任务。
- HttpllProcessor组件负责从客户端连接中读取消息报文,然后开始解析HTTP的请求行、请求头部、请求体。将解析后的报文封装成Request对象,方便后面处理时通过Request对象获取HTTP协议的相关值。
- Mapper组件根据HTTP协议请求行的URL属性值和请求头部的Host属性值匹配由哪个Host容器、哪个Context容器、哪个Wrapper容器处理请求,这个过程其实就是根据请求从Tomcat中找到对应的Servlet,然后将路由的结果封装到Request对象中,方便后面处理时通过Request对象选择容器。
- CoyoteAdaptor组件负责将Connector组件和Engine容器连接起来,把前面处理过程中生成的请求对象Request和响应对象Response传递到Engine容器,调用它的管道
- Engine容器的管道开始处理请求,管道里包含若干阀门(Valve),每个阀门负责某些,处理逻辑。这里用xxxValve代表某阀门,我们可以根据自己的需要往这个管道中添加多个阀门,首先执行这个xxxValve,然后才执行基础阀门EngineValve,它会负责调用Host容器的管道。
- Host容器的管道开始处理请求,它同样也包含若干阀门,首先执行这些阀门,然后执行基础阀门HostValve,它继续往下调用Context容器的管道。
- Context容器的管道开始处理请求,首先执行若干阀门,然后执行基础阀门ContextValve,它负责调用Wrapper容器的管道。
- Wrapper容器的管道开始处理请求,首先执行若干阀门,然后执行基础阀门WrapperValve,它会执行该Wrapper容器对应的Servlet对象的处理方法,对请求进行逻辑处理,并将结果输出到客户端。