Andrew
Andrew

Reputation: 419

Spring boot mvc async execution

I'm trying to execute simplest async REST controller using java.util.concurrent.Callable:

@RequestMapping("/AsyncRequest")
public Callable asyncRequest() {
    return () -> {
        Thread.sleep(3000);
        return "reply";
    };
}

Then I run http://localhost/AsyncRequest two times in browser. The first answer I get in 3 seconds, but the second just after 6 seconds. It seems like that the requests processed not asynchronously. Why this happen?

Upvotes: 4

Views: 1183

Answers (1)

Maksym Prokhorenko
Maksym Prokhorenko

Reputation: 426

Spring Boot behavior really depends on version and configuration.

The following configuration and code works for me:

Starter

package com.stackoverflow.spring.boot.async;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Import;

@Import(WebConfig.class)
@SpringBootApplication
public class WebMain {
  private final SpringApplication application;

  public WebMain() {
    final SpringApplicationBuilder applicationBuilder = new SpringApplicationBuilder(WebMain.class);
    application = applicationBuilder.build();
  }

  public SpringApplication getApplication() { return application; }

  public static void main(final String[] args) {
    new WebMain().getApplication().run(args);
  }
}

Configuration

package com.stackoverflow.spring.boot.async;


import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter implements EmbeddedServletContainerCustomizer
{
  public void customize(final ConfigurableEmbeddedServletContainer container) {}
}

Controller

I've changed sleep time here to 6 second and added some payload into response.

package com.stackoverflow.spring.boot.async;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Date;
import java.util.concurrent.Callable;

@Controller
public class AsyncController {
  @RequestMapping(path = "/AsyncRequest", method = RequestMethod.GET)
  @ResponseBody
  public Callable<String> asyncRequest() {
    return () -> {
      final long currentThread = Thread.currentThread().getId();
      final Date requestProcessingStarted = new Date();

      Thread.sleep(6000L);

      final Date requestProcessingFinished = new Date();

      return String.format(
          "request: [threadId: %s, started: %s - finished: %s]"
          , currentThread, requestProcessingStarted, requestProcessingFinished);
    };
  }
}

pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

      <modelVersion>4.0.0</modelVersion>

      <groupId>com.stackoverflow.spring.boot</groupId>
      <artifactId>async</artifactId>
      <packaging>jar</packaging>
      <version>1.0-SNAPSHOT</version>

      <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>1.4.1.RELEASE</spring-boot.version>
      </properties>

      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-dependencies</artifactId>
          <version>${spring-boot.version}</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>

        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot</artifactId>
          <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
          <version>${spring-boot.version}</version>
        </dependency>
      </dependencies>

      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <inherited>true</inherited>
            <configuration>
              <source>${java.version}</source>
              <target>${java.version}</target>
            </configuration>
          </plugin>

          <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot.version}</version>
            <executions>
              <execution>
                <goals>
                  <goal>repackage</goal>
                </goals>
                <configuration>
                  <classifier>exec</classifier>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>

    </project>

Testing

java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

--
mvn spring-boot:run
...
--
# start following command from different consoles:
curl http://localhost:8080/AsyncRequest

Results

request: [threadId: 33, started: 15:50:28 - finished: 15:50:34]
request: [threadId: 35, started: 15:50:29 - finished: 15:50:35]
request: [threadId: 37, started: 15:50:30 - finished: 15:50:36]

Upvotes: 3

Related Questions