Spring Boot中线程的维护是由servlet容器或Netty负责,所以题主应该问的是servlet容器的线程模型。而Spring Boot是一个自动配置的框架。目前Spring Boot对web开发目前有两种解决方案:
- 传统的web框架基于Spring MVC + Tomcat;
- Spring 5新增的web-reactive框架基于Spring Webflux + Netty;
这两个框架都支持大家熟用的注释,比如@Controller。技术栈可以看官网图:
先不谈web-reactive,通过分析Spring MVC和Tomcat的交互,来浅析一下Spring与servlet容器交互的原理——Spring MVC基于Java EE的Servlet API,Servlet API定义了Servlet容器和具体的servlet代码交互的约定,Spring MVC通过注册一个名为DispatcherServlet的servlet到servlet容器中处理请求,并把实际工作交给Spring提供的组件bean执行。
了解了Spring和servlet容器的交互之后再回到问题。
(1) 首先,这里强调一下连接(TCP)是传输层的,请求(HTTP)是应用层的。
在像 HTTP 这样的Client-Server协议中,会话分为三个阶段:
- 客户端建立一条 TCP 连接(如果传输层不是 TCP,也可以是其他适合的连接)。
- 客户端发送请求并等待应答。
- 服务器处理请求并送回应答,回应包括一个状态码和对应的数据。
从 HTTP/1.1 开始,连接在完成第三阶段后不再关闭,客户端可以再次发起新的请求。这意味着第二步和第三步可以连续进行数次。
(2) 其次真的是一个线程处理一个HTTP请求吗?我觉得这个说法也不准确。
Tomcat支持三种运行模式(BIO, NIO, APR),大致流程均是:
当客户端向服务器建立TCP连接,发送请求,服务器操作系统将该连接放入accept队列,Tomcat在accept队列中接收连接;在连接中获取请求的数据,生成request;调用servlet容器处理请求;返回response,完成一次HTTP会话。
在Tomcat 8.0前默认使用BIO,Tomcat在accept队列中接受TCP连接并获得HTTP Request,从线程池中取出空闲的线程来处理请求,如果无空闲线程则阻塞。
Tomcat 8.0起默认启用NIO模式,在从accept获得request之后,注册到nio.Selector中后不阻塞继续获取连接,Tomcat遍历找到selector中可用的request,再从线程池中取出空闲的线程来处理请求。Tomcat相关的配置参数有:
- acceptCount,当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。
- maxConnections,当Tomcat接收的连接数达到maxConnections时,accept队列中的线程会一直阻塞着。
- maxThreads,线程池线程的最大数量。
所以,无论是BIO,还是NIO,当请求数量大于acceptCount,接收的连接数大于maxConnection时,Tomcat都不会分配线程服务。
总结
一个
请求
过来,系统从线程池
中分配一个可用的线程
来处理当前请求的逻辑。即某一请求的生命周期时间内,一个线程只用来处理一个请求,处理完之后,还给线程池(【特别注意】该线程还在,没被销毁,等待分配给别的页面请求用)
参考:https://www.zhihu.com/question/502043167/answer/2245846786