Tomcat

月伴飞鱼 2024-09-11 11:25:47
服务器
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!

基本架构

总体架构

最顶层是Server:指的是一个Tomcat实例。

一个Server中有一个或者多个Service,一个Service中有多个连接器和一个容器。

  • 连接器与容器之间通过标准的ServletRequest和ServletResponse通信。
img

容器结构

Tomcat设计了4种容器,分别是Engine、Host、Context和Wrapper。

  • 这4种容器不是平行关系,而是父子关系。

Context:

  • 表示一个Web应用程序。

Wrapper:

  • 表示一个Servlet,一个Web应用程序中可能会有多个Servlet。

Host:

  • 代表的是一个虚拟主机,或者说一个站点,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可以部署多个Web应用程序。

Engine:

  • 表示引擎,用来管理多个虚拟站点,一个Service最多只能有一个Engine。
img

请求定位Servlet的过程:

Tomcat会创建一个Service组件和一个Engine容器组件。

在Engine容器下创建两个Host子容器,在每个Host容器下创建两个Context子容器。

由于一个Web应用通常有多个Servlet,Tomcat还会在每个Context容器里创建多个Wrapper子容器。

每个容器都有对应的访问路径。

img

线程池

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

img

Tomcat的自定义类加载器WebAppClassLoader打破了双亲委托机制,首先自己尝试去加载某个类,如果找不到再代理给父类加载器

  • 其目的是优先加载Web应用自己定义的类。
  • 具体实现就是重写ClassLoader的两个方法:findClass和loadClass。
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!