Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 664
Rohit Avatar asked Oct 10 '17 13:10

Rohit


People also ask

What is ResponseBodyEmitter?

ResponseBodyEmitter helps to collect and send the response to the client. It is a controller method return value type for asynchronous request processing where one or more objects are written to the response.


2 Answers

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

like image 162
Dmitry Zlykh Avatar answered Nov 12 '22 05:11

Dmitry Zlykh


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.

like image 25
Giovani Grifante Avatar answered Nov 12 '22 07:11

Giovani Grifante