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();
}
}
}
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? 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.
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.