Number of parallel requests in Spring vs number of http-nio-8080-exec threads

Viewed 4015

I wanted to test parallel requests in my controller. I know that by default Spring can handle 200 parallel requests and we can change it by modifying this property server.tomcat.max-threads

I played a little bit with that value and found interesting thing: When I set it to 3 when I start app I see 3 threads are being created: http-nio-8080-exec-1,2,3 When I set it to 5 I see 5 threads like that. And it continues up to 10 and stops at 10. When I set it to 15 there are still 10 threads of name http-nio-8080-exec. Could someone explain why it never exceeds 10?

If i will make controller like this

@GetMapping("test")
public String test(@RequestParam("skip") boolean skip) throws InterruptedException {
    if(!skip) {
        System.out.println("I'm starting waiting");
        Thread.sleep(10000);
        System.out.println("I stopped waiting");
    }
    return "dd";
}

while having server.tomcat.max-threads=200

make 10 requests like that: http://localhost:8080/test?skip=false

and 11th request like that at the same time: http://localhost:8080/test?skip=true

These first 10 requests should wait 10 secs before returning response but 11th should return immediately - the problem is that 11th request also waits, so it's blocked. Could someone explain how it works? If I have server.tomcat.max-threads set to 200 then I expect that I will be able to handle 200 independent request, am I right?

1 Answers

I was curious about the behaviuor and tested it by myself. Unfortunately I cannot confirm behaviour you are expiriencing. To be able to test simultaneously requests to the service I wrote a simple go programm to prove the responce times. So I started a spring boot service with single rest endpoint with code you posted above and as client following go programm:

package main

import (
    "log"
    "os/exec"
    "time"
)

func main() {
    for i := 0; i< 15; i++ {
        go runCmd(i)
    }
    time.Sleep(time.Second * 50)
}

func runCmd(i int) {
    param := "localhost:8080/test?skip=false"
    if i > 10 {
        param = "localhost:8080/test?skip=true"
    }
    cmd := exec.Command("curl", param)
    log.Printf("Running command %d and waiting for it to finish...", i)
    start := time.Now()
    err := cmd.Run()
    end := time.Now()
    duration := end.Sub(start)
    log.Printf("Command  %d finished within of second %f error: %v", i, duration.Seconds(), err)
}

this program uses curl to send first 10 requests with parameter skip=false and another 5 requests with skip=true. And the output was:

2020/03/08 19:21:18 Running command 14 and waiting for it to finish...
2020/03/08 19:21:18 Running command 9 and waiting for it to finish...
2020/03/08 19:21:18 Running command 3 and waiting for it to finish...
2020/03/08 19:21:18 Running command 10 and waiting for it to finish...
2020/03/08 19:21:18 Running command 7 and waiting for it to finish...
2020/03/08 19:21:18 Running command 0 and waiting for it to finish...
2020/03/08 19:21:18 Running command 12 and waiting for it to finish...
2020/03/08 19:21:18 Running command 6 and waiting for it to finish...
2020/03/08 19:21:18 Running command 5 and waiting for it to finish...
2020/03/08 19:21:18 Running command 13 and waiting for it to finish...
2020/03/08 19:21:18 Running command 11 and waiting for it to finish...
2020/03/08 19:21:18 Running command 2 and waiting for it to finish...
2020/03/08 19:21:18 Running command 1 and waiting for it to finish...
2020/03/08 19:21:18 Running command 4 and waiting for it to finish...
2020/03/08 19:21:18 Running command 8 and waiting for it to finish...
2020/03/08 19:21:18 Command  12 finished within of second 0.035109 error: <nil>
2020/03/08 19:21:18 Command  14 finished within of second 0.035290 error: <nil>
2020/03/08 19:21:18 Command  11 finished within of second 0.040090 error: <nil>
2020/03/08 19:21:18 Command  13 finished within of second 0.041358 error: <nil>
2020/03/08 19:21:28 Command  9 finished within of second 10.034510 error: <nil>
2020/03/08 19:21:28 Command  0 finished within of second 10.034436 error: <nil>
2020/03/08 19:21:28 Command  10 finished within of second 10.037470 error: <nil>
2020/03/08 19:21:28 Command  6 finished within of second 10.042294 error: <nil>
2020/03/08 19:21:28 Command  5 finished within of second 10.042328 error: <nil>
2020/03/08 19:21:28 Command  7 finished within of second 10.045510 error: <nil>
2020/03/08 19:21:28 Command  3 finished within of second 10.045638 error: <nil>
2020/03/08 19:21:28 Command  1 finished within of second 10.049024 error: <nil>
2020/03/08 19:21:28 Command  2 finished within of second 10.053824 error: <nil>
2020/03/08 19:21:28 Command  8 finished within of second 10.053102 error: <nil>
2020/03/08 19:21:28 Command  4 finished within of second 10.053306 error: <nil>

as you can see in the output requests 11 - 14 finished without delay, so they are not blocked. So probably you have to check your test, probably the problem is somewhere in test and not in tomcat configuration.

And the expectation to handle 200 independent requests is true, so should it behave and it behaves that way.

Related