单核心环境下Spring Reactive事件循环与线程内部机制问询
基于单核心机器、Spring Boot + Reactor + Reactive MongoDB + Netty的技术栈,针对你的疑问逐一解答:
问题1:当同时发送1000个请求时,内部会存在多少个事件循环?
在单核心机器的默认配置下,Spring Boot WebFlux依赖Netty运行,会存在2个事件循环线程:
- 1个boss事件循环线程:负责监听端口、接收新的TCP连接;
- 1个worker事件循环线程:负责处理已建立连接的所有I/O操作(包括HTTP请求解析、响应发送,以及Reactive MongoDB的异步I/O回调)。
Reactive MongoDB的异步驱动默认会复用Netty的worker事件循环,不会额外创建独立的事件循环组。
问题2:同时发送1000个请求时,会创建多少个线程?
默认配置下,处理这1000个请求的核心业务线程仅1个(Netty worker事件循环线程),再加上1个Netty boss线程,以及Spring Boot运行必需的少量辅助线程(比如主线程、监控指标线程等,不参与请求的业务处理)。
Reactive模式基于事件循环的非阻塞机制,所有请求的接收、数据库调用的回调都会在同一个worker线程上顺序执行,不会为每个请求单独创建线程。
问题3:若请求由reactive-thread1处理,执行数据库调用时会发生什么?
当reactive-thread1(即Netty worker线程)处理请求并调用Reactive MongoDB的查询方法时,实际只是发起了一个异步I/O请求,并注册好回调函数,随后线程会立即释放,回到事件循环中处理其他待执行的事件(比如其他请求的解析、已完成的I/O回调等)。
当MongoDB完成数据查询后,会将结果事件提交到Netty的事件循环队列中:
- 如果
reactive-thread1处于空闲状态,会立即从队列中取出该事件,执行回调函数、生成Flux响应并发送给客户端; - 如果线程正忙于处理其他任务,这个结果事件会在队列中排队,直到线程空闲后再按顺序处理。
由于事件循环是单线程串行执行,不存在并发执行场景,因此无需额外考虑线程安全问题。
问题4:若每个请求包含需5分钟完成的CPU密集型任务,同时发送1000个请求时会发生什么?
单核心环境下,所有CPU密集型任务都会在唯一的事件循环线程上串行执行。第一个请求的CPU任务会占用线程长达5分钟,后续999个请求都会在事件队列中排队,完全无法得到处理,直到前面的任务全部完成。
这会导致整个应用彻底阻塞,所有后续请求的响应时间会被拉长至数小时,甚至直接超时。Reactive模式的优势仅体现在I/O密集型场景,CPU密集型任务会直接阻塞事件循环,完全破坏非阻塞架构的性能优势。
问题5:使用publishOn(Scheduler.boundedElastic())时,内部会创建多少个线程?
Scheduler.boundedElastic()是Reactor专为处理阻塞/CPU密集型任务设计的调度器,默认配置如下:
- 核心线程数等于CPU核心数,即1;
- 最大线程数为100;
- 任务队列容量为10000。
当同时处理1000个请求时,调度器会先启动1个核心线程处理任务;当核心线程繁忙时,会创建新线程直到达到最大线程数100,剩余900个任务会进入队列等待执行。
但在单核心机器上,100个线程会引发严重的上下文切换开销,反而会大幅降低整体处理性能。
内容的提问来源于stack exchange,提问作者Vaishagkumar techy

