Rohit
Rohit

Reputation: 349

Microservice return response first and then process the request

I am working on a solution for which i am trying to create a microservice which returns response immediately and then processes the request.

I am trying to use Java 8 and Spring for this.

Upvotes: 20

Views: 18183

Answers (2)

Giovani Grifante
Giovani Grifante

Reputation: 472

Here is an example with ExecutorService:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;

@RestController
public class MyController {

    // Instantiate an executor service
    private ExecutorService executor = Executors.newSingleThreadExecutor();

    @PreDestroy
    public void shutdonw() {
        // needed to avoid resource leak
        executor.shutdown(); 
    }

    @GetMapping
    public Object gerUrl(HttpServletRequest request) {
        // execute the async action, you can use a Runnable or Callable instances
        executor.submit(() -> doStuff());    
        return "ok";
    }

    private void doStuff(){}
}

You can use the Executors factory class to build a ExecutorService. Those methods might help you:

java.util.concurrent.Executors
Executors.newSingleThreadExecutor() // jobs are queued and executed by a single thread
Executors.newCachedThreadPool() // new threads are instantiated as needed and cached
Executors.newFixedThreadPool(int nThreads) // user defined number of threads

.

@EnableAsync
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MyApplication.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(MyApplication.class, args);
    }

}


import javax.annotation.PreDestroy;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class AsyncConfiguration extends AsyncConfigurerSupport {

    private ThreadPoolTaskExecutor executor;

    @Override
    public Executor getAsyncExecutor() {
        executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(1000);
        executor.initialize();
        return executor;
    }

    @PreDestroy
    public void shutdownExecutors() {
        executor.shutdown();
    }

}


@Service
public class MyService {

    @Async
    public void doStuff(){
        // Async method
    }

}

Both techniques are quite good, but the first one with ExecutorService give you more control.

Upvotes: 8

Dmitry Zlykh
Dmitry Zlykh

Reputation: 489

This can be achieved in several ways.

In order to return a result from the current thread (a controller in this case) while still doing some long-running operation, you will need another thread.

  • Use Executor directly.

A controller:

@Controller
public class AsyncController {

    private AsyncService asyncService;

    @Autowired
    public void setAsyncService(AsyncService asyncService) {
        this.asyncService = asyncService;
    }

    private ResponseEntity asyncMethod(@RequestBody Object request) {
        asyncService.process(new MyLongRunningRunnable());

        // returns immediately
        return ResponseEntity.ok("ok");
    }
}

And a service:

@Service
public class AsyncService {
    private ExecutorService executorService;

    @PostConstruct
    private void create() {
        executorService = Executors.newSingleThreadExecutor();
    }

    public void process(Runnable operation) {
        // no result operation
        executorService.submit(operation);
    }


    @PreDestroy
    private void destroy() {
        executorService.shutdown();
    }
}

More details can be found here https://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html

  • Another way is to use Spring built-in async capabilities

You can simply annotate a method with @Async, having void or Future return type.

If you still want to supply your own executor, you may implement AsyncConfigurer interface in your spring configuration bean. This approach also requires @EnableAsync annotation.

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return Executors.newSingleThreadExecutor();
    }

}

More on this topic https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html

Upvotes: 17

Related Questions