Tomcat BIO Connector为何性能低下?线程机制深度解析
学习Spring过程中,我好奇多用户同时连接时的多线程工作机制,发现是Tomcat而非Spring负责接收请求并分配线程。Tomcat提供BIO Connector和NIO Connector两种方式,出于性能考量通常选用NIO,但我始终难以理解BIO性能下降的核心原因。
BIO Connector
- 采用阻塞模式工作
- 请求到达时,从线程池分配线程并与请求绑定
- 请求结束后,归还线程
NIO Connector
- 采用非阻塞模式工作
- Poller线程以非阻塞方式遍历Socket Channel,为到达的请求分配线程
核心疑问
- 有说法称BIO因分配线程的空闲时间长、所需线程数多于NIO而影响性能
- NIO要体现优势,需在请求绑定线程后发生阻塞(即线程存在空闲时间)
- 但请求分配给线程后,线程会按Spring容器定义执行逻辑,理论上不应存在空闲时间
相关猜测
猜测一
Tomcat通过socket.accept识别请求。若socket.accept在请求的第一个数据包到达时就识别请求,而非等待请求完整到达,那么分配线程后可能会等待剩余数据包到达,产生延迟。暂不考虑该假设是否正确,仅疑惑此延迟是否小到不足以支撑选用NIO,且切换NIO是否会带来过高开销。
猜测二
若Tomcat为创建的Socket Channel分配线程并以阻塞方式轮询请求,即便无即时连接,线程空闲时间也会很长(因线程已分配,会阻塞至下一次连接)。此时“请求到达时从线程池分配线程,请求处理完成后归还线程”的说法并不正确,因为Socket Channel创建时就分配线程,且通道会持续存在至服务器关闭,线程不会归还至池。若如此,确实需要NIO Connector,但线程池的必要性存疑。
猜测三
BIO Connector由多线程维护,线程数等于创建的Socket Channel数。当所有通道被占用时,会为新请求创建并分配新线程,增加通道数量。BIO Connector的线程与Spring中处理请求的线程完全分离。因此,无论使用BIO还是NIO,Spring容器的线程分配/归还时间及数量无差异,仅Connector自身线程数从N(Socket Channel数)降至1(Selector),行为从阻塞变为非阻塞。
猜测四
BIO在请求到达时分配线程,请求处理完成后不归还线程,需等待连接关闭才释放线程。NIO不为请求绑定线程,而是将请求所需任务入队,分配给线程处理;任务完成后线程直接归还至池,无需等待连接关闭。若有新请求,Poller会再次创建任务入队,分配给空闲线程。
总结困惑
Tomcat因BIO Connector的问题引入NIO Connector,但BIO的问题究竟是什么?若请求到达时分配线程、处理完成后归还,并无明显问题;若Socket Channel创建时分配线程且处理完成后不归还,则确实存在性能问题,但此时线程池毫无意义。Tomcat Connector线程与Spring容器线程是否为独立概念?BIO与NIO的差异是否在于将请求绑定线程还是将请求任务绑定线程?
我刚接触Spring,可能问了很多基础问题,感谢阅读,希望我的疑问表述清晰。
内容的提问来源于stack exchange,提问作者White Piano

