服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

云服务器|WEB服务器|FTP服务器|邮件服务器|虚拟主机|服务器安全|DNS服务器|服务器知识|Nginx|IIS|Tomcat|

服务器之家 - 服务器技术 - Tomcat - Tomcat处理请求的线程模型详解

Tomcat处理请求的线程模型详解

2022-10-21 15:37狂拽酷炫棒棒棒 Tomcat

这篇文章主要为大家详细介绍了Tomcat处理请求的线程模型,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

一、前言

JAVA后端项目,运行在容器tomcat中,由于现在springboot的内置tomcat容器,其默认配置屏蔽了很多对tomcat的认知,但是对tomcat的学习和认识是比较重要的,所以专门查资料加深了理解,本文主要讨论在springboot集成下的tomcat9的请求过程,线程模型为NIO。

二、tomcat结构

Tomcat处理请求的线程模型详解

找了张结构图,每个模块的意思和作用就不详解了,可以搜其他文章

 

三、探讨tomcat是如何处理请求

Tomcat处理请求的线程模型详解

自己画了一个connector的结构

1、初始化

在springboot启动后,org.springframework.context.support.AbstractApplicationContext#finishRefresh,这里进去调用org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start()方法启动TomcatWebServer,初始化tomcat。

Tomcat处理请求的线程模型详解

Tomcat处理请求的线程模型详解

通过这样的调用链到达org.apache.tomcat.util.net.NioEndpoint#startInternal(),进行初始化Endpoint中的Acceptor和Poller,这两者都实现了Runnable接口,初始化后就通过线程start启动了。

2、如何处理客户端请求

Acceptor: 接收器,作用是接受scoket网络请求,并调用setSocketOptions()封装成为NioSocketWrapper,并注册到Poller的events中。注意查看run方法org.apache.tomcat.util.net.Acceptor#run

 @Override
  public void run() {
      int errorDelay = 0;
      try {
          // Loop until we receive a shutdown command
          while (!stopCalled) {
              // Loop if endpoint is paused
              while (endpoint.isPaused() && !stopCalled) {
                  state = AcceptorState.PAUSED;
                  try {
                      Thread.sleep(50);
                  } catch (InterruptedException e) {
                      // Ignore
                  }
              }
              if (stopCalled) {
                  break;
              }
              state = AcceptorState.RUNNING;
              try {
                  //if we have reached max connections, wait
                  endpoint.countUpOrAwaitConnection();
                  // Endpoint might have been paused while waiting for latch
                  // If that is the case, don't accept new connections
                  if (endpoint.isPaused()) {
                      continue;
                  }
                  U socket = null;
                  try {
                      // 等待下一个请求进来
                      socket = endpoint.serverSocketAccept();
                  } catch (Exception ioe) {
                      // We didn't get a socket
                      endpoint.countDownConnection();
                      if (endpoint.isRunning()) {
                          // Introduce delay if necessary
                          errorDelay = handleExceptionWithDelay(errorDelay);
                          // re-throw
                          throw ioe;
                      } else {
                          break;
                      }
                  }
                  // Successful accept, reset the error delay
                  errorDelay = 0;
                  // Configure the socket
                  if (!stopCalled && !endpoint.isPaused()) {
                      // 注册socket到Poller,生成PollerEvent事件
                      if (!endpoint.setSocketOptions(socket)) {
                          endpoint.closeSocket(socket);
                      }
                  } else {
                      endpoint.destroySocket(socket);
                  }
              } catch (Throwable t) {
                  ExceptionUtils.handleThrowable(t);
                  String msg = sm.getString("endpoint.accept.fail");
                  // APR specific.
                  // Could push this down but not sure it is worth the trouble.
                  if (t instanceof Error) {
                      Error e = (Error) t;
                      if (e.getError() == 233) {
                          // Not an error on HP-UX so log as a warning
                          // so it can be filtered out on that platform
                          // See bug 50273
                          log.warn(msg, t);
                      } else {
                          log.error(msg, t);
                      }
                  } else {
                          log.error(msg, t);
                  }
              }
          }
      } finally {
          stopLatch.countDown();
      }
      state = AcceptorState.ENDED;
  }

Poller:轮询器,轮询是否有事件达到,有请求事件到达后,以NIO的处理方式,查询Selector取出所有请求,遍历每个请求的需求,分配给Executor线程池执行。查看org.apache.tomcat.util.net.NioEndpoint.Poller#run()

 public void run() {
          // Loop until destroy() is called
          while (true) {
              boolean hasEvents = false;
              try {
                  if (!close) {
                      hasEvents = events();
                      if (wakeupCounter.getAndSet(-1) > 0) {
                          // If we are here, means we have other stuff to do
                          // Do a non blocking select
                          keyCount = selector.selectNow();
                      } else {
                          keyCount = selector.select(selectorTimeout);
                      }
                      wakeupCounter.set(0);
                  }
                  if (close) {
                      events();
                      timeout(0, false);
                      try {
                          selector.close();
                      } catch (IOException ioe) {
                          log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                      }
                      break;
                  }
                  // Either we timed out or we woke up, process events first
                  if (keyCount == 0) {
                      hasEvents = (hasEvents | events());
                  }
              } catch (Throwable x) {
                  ExceptionUtils.handleThrowable(x);
                  log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
                  continue;
              }
				//查询selector取出所有请求
              Iterator<SelectionKey> iterator =
                  keyCount > 0 ? selector.selectedKeys().iterator() : null;
              // Walk through the collection of ready keys and dispatch
              // any active event.
              while (iterator != null && iterator.hasNext()) {
                  SelectionKey sk = iterator.next();
                  iterator.remove();
                  NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                  //处理请求key
                  if (socketWrapper != null) {
                      processKey(sk, socketWrapper);
                  }
              }
              // Process timeouts
              timeout(keyCount,hasEvents);
          }
          getStopLatch().countDown();
      }

请求过程大致如下图:

Tomcat处理请求的线程模型详解

 

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容! 

原文链接:https://blog.csdn.net/weixin_42382121/article/details/123691256

延伸 · 阅读

精彩推荐
  • TomcatTomcat使用线程池处理远程并发请求的方法

    Tomcat使用线程池处理远程并发请求的方法

    这篇文章主要介绍了Tomcat使用线程池处理远程并发请求的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以...

    Narule4242021-09-18
  • TomcatHBuilderX配置tomcat外部服务器查看编辑jsp界面的方法详解

    HBuilderX配置tomcat外部服务器查看编辑jsp界面的方法详解

    这篇文章主要介绍了HBuilderX配置tomcat外部服务器查看编辑jsp界面的方法,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参...

    以太羊5532021-09-17
  • TomcatLinux下安装配置tomcat

    Linux下安装配置tomcat

    Tomcat是一个轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。对于一个初学者来说,可以这样...

    Tomcat教程网8802021-08-20
  • TomcatUbuntu安装配置tomcat9教程

    Ubuntu安装配置tomcat9教程

    这篇文章主要为大家详细介绍了Ubuntu安装配置tomcat9教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    feipeng88488572021-08-19
  • TomcatEclipse添加Tomcat Server配置的方法步骤

    Eclipse添加Tomcat Server配置的方法步骤

    这篇文章主要介绍了Eclipse添加Tomcat Server配置的方法步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    write610242021-09-03
  • Tomcat腾讯云Ubuntu14.04安装Tomcat8教程

    腾讯云Ubuntu14.04安装Tomcat8教程

    本篇文章主要介绍了腾讯云Ubuntu14.04安装Tomcat8教程,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    倘若光明绿肥红瘦8602021-08-18
  • TomcatTomcat设置maxPostSize实现过程解析

    Tomcat设置maxPostSize实现过程解析

    这篇文章主要介绍了Tomcat设置maxPostSize实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参...

    与李11342021-09-14
  • Tomcat基于IDEA部署Tomcat服务器的步骤详解

    基于IDEA部署Tomcat服务器的步骤详解

    这篇文章主要介绍了基于IDEA部署Tomcat服务器,并在上面创建第一个servlet程序,实现前后端交互,然后做一个简单的四则运算计算器,需要的朋友可以参考下...

    Milo_Luom5792021-09-23