基本架构
总体架构
最顶层是Server:指的是一个Tomcat实例。
一个Server中有一个或者多个Service,一个Service中有多个连接器和一个容器。
- 连接器与容器之间通过标准的ServletRequest和ServletResponse通信。
容器结构
Tomcat设计了4种容器,分别是Engine、Host、Context和Wrapper。
- 这4种容器不是平行关系,而是父子关系。
Context:
- 表示一个Web应用程序。
Wrapper:
- 表示一个Servlet,一个Web应用程序中可能会有多个Servlet。
Host:
- 代表的是一个虚拟主机,或者说一个站点,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可以部署多个Web应用程序。
Engine:
- 表示引擎,用来管理多个虚拟站点,一个Service最多只能有一个Engine。
请求定位Servlet的过程:
Tomcat会创建一个Service组件和一个Engine容器组件。
在Engine容器下创建两个Host子容器,在每个Host容器下创建两个Context子容器。
由于一个Web应用通常有多个Servlet,Tomcat还会在每个Context容器里创建多个Wrapper子容器。
每个容器都有对应的访问路径。
线程池
Tomcat线程池扩展了原生的
ThreadPoolExecutor
。通过重写
execute
方法实现了自己的任务处理逻辑:
- 前
corePoolSize
个任务时,来一个任务就创建一个新线程。- 再来任务的话,就把任务添加到任务队列里让所有的线程去抢,如果队列满了就创建临时线程。
- 如果总线程数达到
maximumPoolSize
,则继续尝试把任务添加到任务队列中去。- 如果缓冲队列也满了,插入失败,执行拒绝策略。
Tomcat在线程总数达到最大数时,不是立即执行拒绝策略。
- 而是再尝试向任务队列添加任务,添加失败后再执行拒绝策略。
默认情况下Tomcat的任务队列是没有限制的,可以通过设置
maxQueueSize
参数来限制任务队列的长度。因为Tomcat属于IO密集型:
- 所以才优先尝试新建线程,线程池满载了,再添加任务到阻塞队列里排队等待。
public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor {
...
public void execute(Runnable command, long timeout, TimeUnit unit) {
submittedCount.incrementAndGet();
try {
//调用Java原生线程池的execute去执行任务
super.execute(command);
} catch (RejectedExecutionException rx) {
//如果总线程数达到maximumPoolSize,Java原生线程池执行拒绝策略
if (super.getQueue() instanceof TaskQueue) {
final TaskQueue queue = (TaskQueue)super.getQueue();
try {
//继续尝试把任务放到任务队列中去
if (!queue.force(command, timeout, unit)) {
submittedCount.decrementAndGet();
//如果缓冲队列也满了,插入失败,执行拒绝策略。
throw new RejectedExecutionException("...");
}
}
}
}
}
类加载器
假如使用JVM默认AppClassLoader来加载Web应用,AppClassLoader只能加载一个Servlet类,在加载第二个同名Servlet类时,AppClassLoader会返回第一个Servlet类的Class实例。
- 在AppClassLoader看来,同名的Servlet类只被加载一次。
因此Tomcat的解决方案是自定义一个类加载器WebAppClassLoader,并且给每个Web应用创建一个类加载器实例。
- Context容器组件对应一个Web应用,因此,每个Context容器负责创建和维护一个WebAppClassLoader加载器实例。
SharedClassLoader
作为WebAppClassLoader的父加载器,专门来加载Web应用之间共享的类。
如果WebAppClassLoader自己没有加载到某个类,就会委托父加载器SharedClassLoader去加载这个类,SharedClassLoader会在指定目录下加载共享类,之后返回给WebAppClassLoader。
CatalinaClassLoader
- 专门来加载Tomcat自身的类。
CommonClassLoader
Tomcat的自定义类加载器WebAppClassLoader打破了双亲委托机制,首先自己尝试去加载某个类,如果找不到再代理给父类加载器
- 其目的是优先加载Web应用自己定义的类。
- 具体实现就是重写ClassLoader的两个方法:findClass和loadClass。