SpringBoot @RequestMapping接口的线程处理机制疑问
SpringBoot 默认
@RequestMapping 请求处理逻辑拆解 基础处理模型
SpringBoot 未做额外配置时,默认使用内嵌Tomcat作为Web容器,请求处理完全遵循Tomcat标准NIO线程模型,和常规并行多线程处理逻辑完全一致:
- 独立Acceptor线程负责监听、接收新的TCP连接,将连接绑定到事件轮询队列
- 固定规模的Worker工作线程池负责处理具体的HTTP请求,默认参数如下:
- 最大工作线程数200
- 最大承载TCP连接数8192
- 工作线程全部被占用时,新连接会进入长度为100的等待队列,不会直接被拒绝
也就是说,只要并发请求数不超过200,所有请求都会被并行处理,不存在服务端层面的串行排队逻辑。你最初预期的“5个请求同时触发sleep日志、20秒后同时结束”的并行效果,本身是完全符合服务端实际行为的。
实测日志差异的真实成因
你看到的有时间差、串行输出的日志,根源不在服务端,而在Chrome浏览器的客户端请求限制:
Chrome对同域名下的顶层导航请求(也就是在地址栏输入地址回车、触发整页加载的请求)有严格的发送队列规则:如果同域名的前一个导航请求还处于pending状态(未收到服务端响应),后续的同域导航请求会被暂存在浏览器本地队列,不会立刻向外发送。
结合你输出的日志时间线可以完全匹配这个逻辑:
- 14:30:35 第一个标签页的请求发出,到达服务端后触发第一条
sleep begin,开始20秒的线程休眠 - 剩下4个标签页的请求,哪怕你在第一个请求等待的20秒内都按下了回车,也会被Chrome在本地压20秒,根本没发送到服务端
- 14:30:55 第一个请求休眠结束、返回响应给浏览器,Chrome才会按照之前的点击顺序,依次把本地队列里的4个请求发出去,因为浏览器自身的请求调度间隔,就出现了55分、57分、59分、00分依次触发
sleep begin的日志 - 每个请求到达服务端后都会执行20秒休眠,所以对应的
sleep end日志依次在14:31:15、14:31:17、14:31:19、14:31:20输出
可以通过两个简单测试验证这个结论:
- 不用Chrome标签页,改用5个独立终端窗口同时执行
curl http://localhost:8080/hello,会看到服务端立刻输出连续5条sleep begin,20秒后输出连续5条sleep end,完全符合最初的并行预期 - 打开Chrome开发者工具的Network面板,可以看到后4个请求的总耗时里,有接近20秒是在浏览器本地队列等待的时间,真正到达服务端后的处理时间只有20秒
认知偏差说明
你之所以会觉得服务端的线程逻辑“既不像单线程、也不像常规并行模型”,本质是把浏览器客户端的本地排队行为,误判成了服务端的处理逻辑:服务端从始至终都是按多线程并行的规则处理到达的请求,所有的串行延迟都是浏览器侧的调度限制导致的,和@RequestMapping的映射处理逻辑没有关联。
内容的提问来源于stack exchange,提问作者KouAkira Chin
相关产品推荐
相关产品推荐

