3

I am relatively new to Spring Boot and started with the very simple example from their getting started site, which is (on the controller side):

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Spring Boot!";
    }

}

What I want now, is that multiple (potentially long running) requests of the same controller can be served in parallel.

Since I already learned that a @RestController will be instantiated as singleton, it is clear for me, that multiple requests (which are handled by the same method) will be processed sequentially. Update: My bad: I thought it was related to the fact that the controller was a singleton. But true: Why should it not be able to run in parallel then?

So I changed the example from above as follows so that a new instance of the controller is created on every request and with some means of inspecting what's actually happening:

@RestController
@Scope(value = "request")
public class HelloController {

    private static AtomicInteger count = new AtomicInteger(0);

    public HelloController() {
        count.incrementAndGet();
    }

    @PostConstruct
    public void init() {
        System.out.println("start request " + count);
    }

    @PreDestroy
    public void onDestroy() {
        System.out.println("end request " + count);
    }

    @RequestMapping("/")
    public String index() throws InterruptedException {
        LocalDateTime now = LocalDateTime.now();
        TimeUnit.SECONDS.sleep(15);
        System.out.println(now);
        return "Greetings from Spring Boot! " + now + "  " + count.get();
    }

}

Now I would expect to see that the requests are handled in parallel in about 15 seconds, but in fact I can only see that it is obviously processed sequentially and that it takes 30 seconds (on stdout):

start request 1
2017-02-11T14:19:47.429
end request 1
start request 2
2017-02-11T14:20:02.467
end request 2

So my question is: How can I achieve that such requests are processed in parallel since it is obviously not sufficient to create an instance for each request?


Little remark: I already tried using the @Asnync annotation in combination with @EnableAsync for the application class, but this seems to be a "fire and forget" such that I cannot get a response to show on the client side.

Several entries here on stackoverflow (eg. this and this) were interesting, but did not answer my question either, neither did this tutorial about asynchronous methods.


Update: Since several people pointed out that the issue may be related to the way I tested, I tried to run it with a different browser. Interestingly enough I experienced the same issues on Chrome as well as on Firefox. But when doing one request from each, it showed the expected behavior (serving the requests in parallel) – so I was fooled by the browers ...

Community
  • 1
  • 1
philonous
  • 3,621
  • 3
  • 27
  • 24
  • How do you perform parallel requests? – Andremoniy Feb 11 '17 at 13:43
  • how do you you test to call your HelloController endpoint ? By using your browser(open 2 tab) or unit-testing (use threading)? – algojava Feb 11 '17 at 13:46
  • 1
    "Since I already learned that a @RestController will be instantiated as singleton, it is clear for me, that multiple requests (which are handled by the same method) will be processed sequentially." This assertion is false. In a servlet container, each request is processed in an different thread. Unless your controller method is synchronized, requests will be processed by your controller in parrallel – gregfqt Feb 11 '17 at 13:47
  • Moreover, as your controller is a singleton, `@PostConstruct` and `@PreDestroy` should be called only once during your container lifecycle – gregfqt Feb 11 '17 at 13:49
  • @gregfqt: he modify the Controller to use scope = "request" – algojava Feb 11 '17 at 13:52
  • @algojava oh, I didn't read carefully ! – gregfqt Feb 11 '17 at 13:55

4 Answers4

3

You wrote:

Since I already learned that a @RestController will be instantiated as singleton, it is clear for me, that multiple requests (which are handled by the same method) will be processed sequentially.

"...will be processed sequentially" - this is false statement. I even have no idea on what basis it was made. Until you make method synchronized (or will use other lock technics), it will be accessible by multiply threads simultaneously.

In second case you've just configured it to create new instance for each request. I have only one idea explaining why do you receive sequential results in debugger: you perform this requests from browser sequentially as well.

If you want to test how multiply requests are perfectly handled even by 1st case, just open separate tabs in browser and start requests almost at the same time.

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • Thanks for answering! :) I was testing this within two tabs of the same browser and starting one request right after the other (definitely within the 15 seconds time frame), so the requests have been done in parallel, I would assume, haven't they?! But I think you are right with pointing out that one of my assumptions was wrong ... thanks! – philonous Feb 11 '17 at 15:42
0

The controller being a singleton has nothing to do with whether it can be accessed concurrently.

You really shouldn't need to use request based scoping unless you have some mutable field that cant be shared across threads, but implementing something like that is probably just bad design.

I imagine the reason you are seeing the two requests take 30 seconds is because you arent actually making concurrent requests, rather you are waiting for the first request to complete before starting the second.

Magnus
  • 7,952
  • 2
  • 26
  • 52
  • First of all: Thanks for your answer! :) Well, I was making the two requests in two separate browser windows, the second one around two or three seconds after the first one. And I would expect that both requests are then handled within around 18 seconds – or is my assumption wrong here? – philonous Feb 11 '17 at 15:35
  • Yeah so if your request sleeps for 15 seconds, and it takes you three seconds to go between each browser tab then the whole process should take 18 seconds. – Magnus Feb 12 '17 at 05:37
0

I am using Spring since 2009, I know that their Controller is Singleton. But it doesn't mean they process your request sequentially(wait for one to finish, then execute the next one).

The example codes from Spring Boot website are able to handle parallel request. It will create singleton instance of HelloController but Spring is able to call index() multiple times at the same time(parallel).

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Spring Boot!";
    }

}
rohanagarwal
  • 771
  • 9
  • 30
algojava
  • 743
  • 4
  • 9
0

Actually, The problem is not from your controller, the tomcat server create a thread for each request you send, the problem is from the browser itself if you send 2 requests to the same endpoint the browser stalls the second request until the first one get the response "I don't know why or how to fix it", and you will find that the actual time server take is the time form sending the request to getting the first byte of the response TTFB time check this screenshot from browser Networking of the second request, you can use other browser or another device connected to your network to test sending 2 requests at the same time

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 19 '21 at 16:37