Spring Webflux Reactor中reactor-http-nio与boundedElastic线程池疑问
问题1:为什么携带请求体的POST方法由reactor-http-nio线程池处理?
Spring WebFlux底层基于Netty实现时,请求的报文解析、请求体读取、参数反序列化绑定逻辑默认都在Netty的IO线程(也就是你自定义配置的reactor-http-nio线程)上执行。
- 无请求体的请求(GET、无body的POST)不需要额外读取解析请求体,Spring WebFlux会直接将后续的Controller方法调用调度到默认的业务线程池
Schedulers.boundedElastic上执行,所以你会看到这两类请求的入口日志打在boundedElastic线程上。 - 携带请求体的POST请求,需要先在Netty IO线程上完成请求体读取、反序列化、参数绑定,完成后直接在当前IO线程调用你的Controller方法,所以入口的
save request arrived日志就打在了reactor-http-nio线程上,此时还没有触发后续响应流的订阅逻辑。
问题2:http-nio属于规模更小的线程池,为何我认为属于阻塞代码的带请求体POST请求会分配到该线程池执行?
你的阻塞代码(userRepository.save)根本没有在reactor-http-nio线程上执行:
- 你把阻塞的save操作封装在了
Mono.fromCallable内部,并且指定了subscribeOn(Schedulers.boundedElastic()),这个配置会让整个newUser流的订阅和执行逻辑全部切换到boundedElastic线程池。 reactor-http-nio线程只执行了Controller方法入口的日志打印、构造响应流对象这两步无阻塞的轻量操作,完成后就被释放回IO线程池处理其他网络请求,不会被阻塞逻辑占用。
问题3:代码中return newUser.subscribeOn(Schedulers.boundedElastic())的写法是否合理,还是应该保留在原有线程执行?
这个写法是完全合理的,甚至是必须的:
- 从你的代码写法能判断
userRepository是阻塞式的持久层实现(比如Spring Data JPA),这类阻塞IO操作绝对不能放在Netty IO线程执行,否则很容易把数量极少的IO线程全部占满,导致整个服务无法处理新的网络请求。 - 只有当你使用响应式持久层实现(比如R2DBC对应的
ReactiveCrudRepository)时,才不需要手动切换线程池,响应式驱动本身不会阻塞线程,直接在IO线程执行也不会有问题。
内容的提问来源于stack exchange,提问作者Rafael Fogel
相关产品推荐
相关产品推荐

