简体   繁体   中英

Spring Boot REST - requests are not executing with ThreadPoolTaskExecutor configuration

I am trying to develop a spring boot app. I have written all core implementations in core java without spring framework. I am using that jar in this spring boot app. I would like to manage the concurrency of my rest controller. So, configured ThreadPoolTaskExecutor accordingly in the main class. Ideally, I want only 2 concurrent requests to get into the execute() method, which I annotated Async . I was testing for 2 concurrent requests at a time but I see in the log that my requests are entering execute() all at once. All the tasks are memory intensive. So those are failing with heap memory issues. I am trying to figure out the ideal concurrency number. I would like to know if my configuration is correct or am I missing something? Thank you.

Here's my main class:

@SpringBootApplication
@EnableAsync
public class RestapiApplication implements AsyncConfigurer {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(RestapiApplication.class, args);
        System.out.println("Rightdata Middleware ready to accept requests:");
    }

    @Bean(name = "executor1")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(2);
        taskExecutor.setCorePoolSize(2);
        taskExecutor.setThreadNamePrefix("LULExecutor-");
        taskExecutor.setQueueCapacity(100);
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

And here's my REST controller:

@RestController
@RequestMapping("/end2end")
public class End2EndRestController {

    /**
     * The log.
     */
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @RequestMapping(method = RequestMethod.POST)
    public JSONObjectPOJO process(@RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {

        final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
        final ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    execute(jsonObjectPOJO);
                } catch (Exception e) {
                    e.getMessage();
                }
            }});
            executor.shutdown();
            return jsonObjectPOJO;
        }

    @Async("executor1")
    private void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future<?> futureTarget;
        Future<?> futureSource;
        futureSource = processSource(executorService);
        futureTarget = processTarget(executorService);
        manageSourceProcessingResults(futureSource);
        manageTargetProcessingResults(futureTarget);
        executorService.shutdown();
        //Do rest of the tasks.
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    protected Future<?> processSource(executorService){
        //Get appropriate class instance with call() - coreActionClass.
        Future<?> futureSource = executorService.submit(coreActionClass);
        return futureSource;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    protected Future<?> processTarget(executorService){
        //Get appropriate class instance with call() - coreActionClass.
        Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
        return futureTarget;
    }

    private void manageSourceProcessingResults(Future<?> futureSource) {
        try{
            futureSource.get();
        } catch(Exception e){
            e.printStackTrace();
        }
    }

    private void manageTargetProcessingResults(Future<?> futureTarget) {
        try{
            futureTarget.get();
        } catch(Exception e){
            e.printStackTrace();
        }
    }
}

UPDATE- 1:

I have now changed the code to following:

@RestController
@RequestMapping("/end2end")
public class End2EndRestController {

    /**
     * The log.
     */
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @RequestMapping(method = RequestMethod.POST)
    public JSONObjectPOJO process(@RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {

        final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
        final ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    execute(jsonObjectPOJO);
                } catch (Exception e) {
                    e.getMessage();
                }
            }});
            executor.shutdown();
            return jsonObjectPOJO;
        }    
}

And AsyncService class:

public class AsyncService {

    @Async("executor1")
    public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Future<?> futureTarget;
        Future<?> futureSource;
        futureSource = processSource(executorService);
        futureTarget = processTarget(executorService);
        manageSourceProcessingResults(futureSource);
        manageTargetProcessingResults(futureTarget);
        executorService.shutdown();
        //Do rest of the tasks.
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    protected Future<?> processSource(executorService){
        //Get appropriate class instance with call() - coreActionClass.
        Future<?> futureSource = executorService.submit(coreActionClass);
        return futureSource;
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    protected Future<?> processTarget(executorService){
        //Get appropriate class instance with call() - coreActionClass.
        Future<?> futureTarget = executorService.submit(coreActionClass); //callable method in core.
        return futureTarget;
    }

    private void manageSourceProcessingResults(Future<?> futureSource) {
        try{
            futureSource.get();
        } catch(Exception e){
            e.printStackTrace();
        }
    }

    private void manageTargetProcessingResults(Future<?> futureTarget) {
        try{
            futureTarget.get();
        } catch(Exception e){
            e.printStackTrace();
        }
    }
}
  1. My understanding is that when I configure maxpoolsize(2) no more than 2 requests would be in the execute() method at one time. For a new request to enter, one of the earlier requests has to complete its execution. Is my understanding correct? Would the async apply to the inner executor service?
  2. I am of the view that at one time only 2 requests are handled and each of those requests can spawn 2 different threads and complete its task. Please clarify.

I see two problems.

1) In your process method you are creating a new ExecutorService. This is not needed. Instead just call the execute method after the jsonObjectPOJO is retrieved.

2) You cannot use @Async int he same class that it is implemented. You'll need to create a new class, lets called MyAsyncService to contain the @Async method. This is because of the aspect orientated programming that is going on under the covers.

Check out this link for more info. Below is a quote from the link.

First – let's go over the rules – @Async has two limitations:

it must be applied to public methods only self-invocation – calling the async method from within the same class – won't work The reasons are simple – the method needs to be public so that it can be proxied. And self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly.

EDIT 1:

@RestController
@RequestMapping("/end2end")
public class End2EndRestController {

@AutoWired
AsyncService asyncService;

private final Logger log = LoggerFactory.getLogger(this.getClass());
@RequestMapping(method = RequestMethod.POST)
public JSONObjectPOJO process(@RequestBody String end2EndScenarioString) throws InterruptedException, ExecutionException {
    final JSONObjectPOJO jsonObjectPOJO = convertToJavaObject(end2EndScenarioString);
    asyncService.execute(jsonObjectPOJO);
    return jsonObjectPOJO;
}




public class AsyncService {

    @Async("executor1")
    public void execute(JSONObjectPOJO jsonObjectPOJO) throws Exception {
        //No Futures, no ExecutorServices, just process that request.
    }

}

By creating and configuring the ThreadPoolTaskExecutor to use only 2 threads, you have accomplished your goal.

EDIT2: Spring @Async limit number of threads

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM