3

I'm using Spring Webflux and as I understand it, by using this, the thread used for receiving request and the one used for response should be different. However, whether I use netty or undertow, I end up using the same thread.

My app is a simple crud app with MySQL DB. I'm not using r2dbc but a jdbc coupled with Executor and Scheduler.

As shown in the log below, request is handled by thread [ XNIO-1 I/O-6] and the response is given by the same one. By this, I'm assuming the thread is blocked until db operation has finished. How can I fix this?

Here's the log

2019-07-23 17:49:10.051  INFO 132 --- [           main] org.xnio                                 : XNIO version 3.3.8.Final
2019-07-23 17:49:10.059  INFO 132 --- [           main] org.xnio.nio                             : XNIO NIO Implementation Version 3.3.8.Final
2019-07-23 17:49:10.114  INFO 132 --- [           main] o.s.b.w.e.undertow.UndertowWebServer     : Undertow started on port(s) 8080 (http)
2019-07-23 17:49:10.116  INFO 132 --- [           main] c.n.webflux.demo.WebfluxFunctionalApp    : Started WebfluxFunctionalApp in 1.262 seconds (JVM running for 2.668)
2019-07-23 17:49:10.302 DEBUG 132 --- [   XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter    : [4c85975] HTTP GET "/api/findall"
2019-07-23 17:49:10.322 DEBUG 132 --- [   XNIO-1 I/O-6] s.w.r.r.m.a.RequestMappingHandlerMapping : [4c85975] Mapped to public reactor.core.publisher.Mono<java.util.List<com.webflux.demo.model.TypeStatus>> com.webflux.demo.controller.MonitoringController.findAll()
2019-07-23 17:49:10.337 DEBUG 132 --- [   XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler  : Using 'application/json;charset=UTF-8' given [*/*] and supported [application/json;charset=UTF-8, application/*+json;charset=UTF-8, text/event-stream]
2019-07-23 17:49:10.338 DEBUG 132 --- [   XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler  : [4c85975] 0..1 [java.util.List<com.webflux.demo.model.TypeStatus>]
2019-07-23 17:49:10.347  INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-07-23 17:49:10.785  INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-07-23 17:49:10.838 DEBUG 132 --- [pool-1-thread-1] org.springframework.web.HttpLogging      : [4c85975] Encoding [[com.webflux.demo.model.TypeStatus@7b4509cb, com.webflux.demo.model.TypeStatus@22676ebe, (truncated)...]
2019-07-23 17:49:10.949 DEBUG 132 --- [   XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter    : [4c85975] Completed 200 OK

Also my dao is

@Repository
public class TypeStatusJdbcTemplate {
    private JdbcTemplate jdbcTemplate;

    public TypeStatusJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    private final static String SQL_FIND_ALL = "select * from `monitoring`.`type_status` limit 3";


    public List<TypeStatus> findAll() {
        return jdbcTemplate.query(SQL_FIND_ALL,
                new TypeStatusMapper());
    }
}

service is

@Service
public class MonitoringService {
    private final Scheduler scheduler;
    private TypeStatusJdbcTemplate repository;

    public MonitoringService(Scheduler scheduler, TypeStatusJdbcTemplate repository) {
        this.scheduler = scheduler;
        this.repository = repository;
    }

    public Mono<List<TypeStatus>> findAll() {
        return Mono.fromCallable(repository::findAll).subscribeOn(scheduler);
    }

}

controller is

@RestController
@RequestMapping("/api")
public class MonitoringController {
    private final MonitoringService monitoringService;
    private static final Logger logger = LoggerFactory.getLogger(MonitoringController.class);

    public MonitoringController(MonitoringService monitoringService) {
        this.monitoringService = monitoringService;
    }

    @GetMapping(value="/findall")
    public Mono<List<TypeStatus>> findAll() {
        return monitoringService.findAll();
    }
}

main file (showing scheduler)

@SpringBootApplication
public class WebfluxFunctionalApp {
    public static void main(String[] args){
        SpringApplication.run(WebfluxFunctionalApp.class, args);
    }


    @PostConstruct
    public void init(){
        // Setting Spring Boot SetTimeZone
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    }


    @Bean
    public Scheduler jdbcScheduler() {
        return Schedulers.fromExecutor(Executors.newFixedThreadPool(30));
    }
}
yhware
  • 502
  • 3
  • 13
  • 26
  • What exact type of `Scheduler` is autowired? – arap Jul 23 '19 at 18:04
  • Anyway, by using `subscribeOn`, all blocking database work is delegated to another thread, that's why `XNIO-1 I/O-6` doesn't get blocked. Unless you don't autowire netty scheduler ;) – arap Jul 23 '19 at 18:11
  • I added the code showing the scheduler. I don't quite understand what you mean by the second comment. I'm expecting XNIO-1 I/O-6 to be NOT blocked. However, I assumed it IS blocked since it's used to process request and response as well, instead of another thread taking on the job. – yhware Jul 24 '19 at 00:33
  • Maybe your understanding of "should" is too strict...try more load! ...And: I *see* (Mono) logging from the 'pool-' threads! – xerx593 Jul 24 '19 at 00:45
  • Try to place something like `System.out.println("callable thread: "+Thread.currentThread().getName());` in `Mono.fromCallable(()->{})` block. You will see something like `callable thread: pool-1-thread-1`. It shows that request is handled by XNIO-1 thread, then it passes control to thread from thread pool, and when response is ready, it's passed back to NIO-1. I think that pretty much answers you intended question - XNIO-1 is not blocked, because all blocking work is executed on another thread, and XNIO-1 is used only to handle request and response. – arap Jul 24 '19 at 08:10

1 Answers1

2

Thread execution does not always have to be different threads. Taken from the Reactive documentation:

Reactive Schedulers

Obtaining a Flux or a Mono doesn’t necessarily mean it will run in a dedicated Thread. Instead, most operators continue working in the Thread on which the previous operator executed. Unless specified, the topmost operator (the source) itself runs on the Thread in which the subscribe() call was made.

So there is nothing that says that it has to be a new thread.

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54