sidknot
sidknot

Reputation: 31

Spring Webflux - Server/Client threads utilisation

I'm playing with Spring Webflux (2.0.3.RELEASE) with Netty and try to understand how threads are utilised by server and web client. I wrote some piece of code with chain of http calls with WebClient. I suspected that all calls are non-blocking but I can't figure out why only one request went through entire chain. This is the code and log output below:

public class DemoApplication {

private WebClient webclient = WebClient.create("http://localhost:8080/");

public static void main(String[] args) throws Exception {
    new DemoApplication().startServer();
}

public void startServer() throws Exception {
    RouterFunction<ServerResponse> route = routingFunction();
    HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
    ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
    HttpServer server = HttpServer.create("127.0.0.1", 8080);
    server.newHandler(adapter).block();
    Thread.sleep(1000000);
}

public RouterFunction<ServerResponse> routingFunction() throws Exception {
    return route(path("/1"), req -> ok().body(fromPublisher(get1(), String.class)))
            .andRoute(path("/2"), req -> ok().body(fromPublisher(get2(), String.class)))
            .andRoute(path("/3"), req -> ok().body(fromPublisher(get3(), String.class)));
}

public Mono<String> get1() {
    System.out.println("---------REQUEST---------");
    System.out.println("1: " + Thread.currentThread());
    return webclient.get().uri("2").retrieve().bodyToMono(String.class);
}

public Mono<String> get2() {
    System.out.println("2: " + Thread.currentThread());
    return webclient.get().uri("3").retrieve().bodyToMono(String.class);
}

public Mono<String> get3() {
    System.out.println("3: " + Thread.currentThread());
    try {
        Thread.sleep(1250000); // simulate thread somehow got blocked
    } catch (InterruptedException e) {

    }
    return Mono.just("test");
}
}

I made a 4 calls to localhost:8080/1 and get following output. Only one request managed to reach third method. I expected that when one thread got blocked then other three will be able to handle other requests but they didn't. Whole thread pool consisted of 4 threads (same as number of cores).

---------REQUEST---------
1: Thread[reactor-http-nio-2,5,main]
2: Thread[reactor-http-nio-4,5,main]
3: Thread[reactor-http-nio-2,5,main]
---------REQUEST---------
1: Thread[reactor-http-nio-3,5,main]
2: Thread[reactor-http-nio-1,5,main]
---------REQUEST---------
1: Thread[reactor-http-nio-3,5,main]
2: Thread[reactor-http-nio-1,5,main]
---------REQUEST---------
1: Thread[reactor-http-nio-3,5,main]
2: Thread[reactor-http-nio-1,5,main]

Could you please explain that behaviour?

--------EDIT-------

Explanation: https://groups.google.com/forum/#!topic/netty/1kAS-FJWGRE

Upvotes: 3

Views: 2280

Answers (1)

Brian Clozel
Brian Clozel

Reputation: 59221

You already know that, but other developers reading this: you should never use blocking operations in a Reactor application - or if you do, you should schedule that work on an elastic Scheduler and be aware of the tradeoffs.

If you ever want to simulate a remote service taking a long time to respond, you can use delay* operators for that without artificially blocking a Thread. In this case, I guess you'd like to simulate the fact that part of your app is using blocking I/O in a reactive pipeline.

I think the behavior you're seeing here has something to do with the fact that the server is calling itself and that both WebClient and the Netty server are sharing the same EventLoopGroup. I don't know about the implementation details of work stealing in that case.

I've simplified this example with a blocking handler like this:

@Bean
public RouterFunction<ServerResponse> routingFunction() throws Exception {
    return route(all(), this::handler);
}

Mono<ServerResponse> handler(ServerRequest request) {
    System.out.println("---------REQUEST---------");
    System.out.println(Thread.currentThread());
    try {
        Thread.sleep(1250000); // simulate thread somehow got blocked
    } catch (InterruptedException e) {

    }
    return ServerResponse.ok().build();
} 

In this case, calling that server with a curl client shows the following behavior, as expected (on a 8 cores laptop).

---------REQUEST---------
Thread[reactor-http-nio-2,5,main]
---------REQUEST---------
Thread[reactor-http-nio-3,5,main]
---------REQUEST---------
Thread[reactor-http-nio-4,5,main]
---------REQUEST---------
Thread[reactor-http-nio-5,5,main]
---------REQUEST---------
Thread[reactor-http-nio-6,5,main]
---------REQUEST---------
Thread[reactor-http-nio-7,5,main]
---------REQUEST---------
Thread[reactor-http-nio-8,5,main]
---------REQUEST---------
Thread[reactor-http-nio-1,5,main]

Upvotes: 2

Related Questions