Calling java methods asynchronous using spring

As consumers of websites demand fast page rendering times we as engineers need to continue to look for ways to make systems perform. Let us examine how to make asynchronous native java calls within a spring container.

Detailed Video Notes

Making async calls can be very powerful but before running off integrating async calls within your java application it is important to know that it may not fix your bottleneck. In async design you are only as good as your fastest data call for instance, if you have a total of 5 calls, 3 render in 5 seconds while 2 render in 500 milliseconds, the best you can achieve running asynchronous is 5 seconds. Additional performance tuning may need to be performed at the various levels in your application such as your data access layer, network or application layer.

Getting started

[0:30]

For our exercise we will make a GET rest request to a url that kicks off four different method calls with a delay of 500 milliseconds. This pattern could be applied to any service or data access object. If you are making requests against a database be sure you have configured your connection pools appropriately.

We follow the spring boot tutorial create a project through spring initializr web interface selecting web project dependencies. Downloading and importing into eclipse we will add guava dependency.

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

Application configuration

[0:58]

Lets add @EnableAsync to our Application, which is an annotation that turns on springs async method execution. This annotation has similar proxying behaviors like @Cacheable. One thing to note, if you are calling multiple methods wrapped with @Async within the same class you will need to refactor the method into a new class due to the proxying behaviors.

Since we want to customize the java.util.concurrent.Executor we will implement AsyncConfigurer when doing so we are required to provide the executor itself and let spring know how to handle exceptions. Overriding getAsyncExecutor method we will return a ThreadPoolTaskExecutor a class that will manage the thread pool. Next returning SimpleAsyncUncaughtExceptionHandler in getAsyncUncaughtExceptionHandler will log any errors occurs.

@SpringBootApplication
@EnableAsync
public class Application implements AsyncConfigurer {

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

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setThreadNamePrefix("LULExecutor-");
        taskExecutor.initialize();
        return taskExecutor;
    }

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

}

Starting up our application we will see async has been configured and we can start wrapping our methods with @Async.

2015-01-31 06:29:07.756  INFO 8940 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.scheduling.annotation.ProxyAsyncConfiguration' of type [class org.springframework.scheduling.annotation.ProxyAsyncConfiguration$$EnhancerBySpringCGLIB$$620ff932] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

Websphere thread management

For our example ThreadPoolTaskExecutor is being used to manage threads but if you running in a container you should consider allowing it to handle thread management. For instance, web sphere has work managers that can be configured by using WorkManagerTaskExecutor. Below is the bean and dependency to include in your springframework application.

@Bean
public TaskExecutor taskExecutor() {
    WorkManagerTaskExecutor executor = new WorkManagerTaskExecutor();
    executor.setWorkManagerName(name); //set up in web sphere admin workmanager
    return executor;
}
<dependency>
    <groupId>commonj</groupId>
    <artifactId>commonj-tmw</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

Note: When initially setting up async with websphere I included the following dependency by mistake which resulted in the following error:

<dependency>
    <groupId>com.ibm.ws.prereq</groupId>
    <artifactId>commonj-twm</artifactId>
    <version>1.1</version>
</dependency>
org.springframework.jndi.TypeMismatchNamingException: Object of type [class com.ibm.ws.asynchbeans.WorkManagerImpl] available at JNDI location [default] is not assignable to [commonj.work.WorkManager]

Creating a service

[1:51]

After set up and application configuration is completed we will create MySampleService class that will contain callAsync method. It is flagged with spring’s @Async annotation that will trigger spring to run the execution on a separate thread if the application is set up to. It also has a return type of Future which is requirement to run methods asynchronous.

Filling in the method we will use guava's Stopwatch as a timer and Thread.sleep(500) to delay the execution of the method by 500 milliseconds. Finally returning a AsyncResult which is a wrapper object that implements ListenableFuture which spring borrowed from guava.

@Component
public class MySampleService {

    private final static Logger LOGGER = Logger
            .getLogger(MySampleService.class);

    @Async
    public Future<Long> callAsync(int taskCall) throws InterruptedException {

        Stopwatch stopwatch = Stopwatch.createStarted();

        LOGGER.info("task " + taskCall + " starting");

        Thread.sleep(500);

        stopwatch.elapsed(TimeUnit.MILLISECONDS);

        LOGGER.info("task " + taskCall + "completed in " + stopwatch);

        return new AsyncResult<Long>(stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

}

Creating a controller

[2:25]

After creating the lower level class we call it by exposing a url. Let's break down the taskExecutor controller method. Guava's Stopwatch will be used to calculate the duration of the execution of the method. Next four asyncResult* variables will be created by calling mySampleService.callAsync passing in number that represents execution order. Once the method executes processing immediately starts on a background thread while the main thread continues execution. It isn't until asyncResult.get() that the main thread will fetch the results or if it is still processing it will wait until it completes.

Two usage notes, if you are running multiple async method calls you should put the slowest call first as it is kicked off in the order of execution. Second, if ever have questions while debugging if your processes is running asynchronously you could remove the @EnableAsync in the Application class and rerun your application.

Let's fire up our server and hope that it executes in less than 2 seconds.

@RestController
public class MyAsyncController {

    private final static Logger LOGGER = Logger.getLogger(MyAsyncController.class);

    @Autowired private MySampleService mySampleService;

    @RequestMapping(value="/", produces = {
            MediaType.TEXT_HTML_VALUE},
            method = RequestMethod.GET)
    public String taskExecutor() throws InterruptedException, ExecutionException {

        Stopwatch stopwatch = Stopwatch.createStarted();

        Future<Long> asyncResult1 = mySampleService.callAsync(1);
        Future<Long> asyncResult2 = mySampleService.callAsync(2);
        Future<Long> asyncResult3 = mySampleService.callAsync(3);
        Future<Long> asyncResult4 = mySampleService.callAsync(4);

        LOGGER.info("result 1 took: " + asyncResult1.get());
        LOGGER.info("result 2 took: " + asyncResult2.get());
        LOGGER.info("result 3 took: " + asyncResult3.get());
        LOGGER.info("result 4 took: " + asyncResult4.get());

        stopwatch.elapsed(TimeUnit.MILLISECONDS);

        return "time it took to perform work " + stopwatch;
    }
}

Output

com.levelup.MySampleService : task 1 starting
com.levelup.MySampleService : task 4 starting
com.levelup.MySampleService : task 2 starting
com.levelup.MySampleService : task 3 starting
com.levelup.MySampleService : task 2 completed in 504.9 ms
com.levelup.MySampleService : task 1 completed in 504.8 ms
com.levelup.MySampleService : task 4 completed in 504.8 ms
com.levelup.MySampleService : task 3 completed in 504.8 ms
com.levelup.MyAsyncController : result 1 took: 508
om.levelup.MyAsyncController  : result 2 took: 508
com.levelup.MyAsyncController : result 3 took: 508
com.levelup.MyAsyncController : result 4 took: 508

time it took to perform work 515.1 ms

As we continue to shift towards performance as a feature it is important to understand how async calls can be used to help. Spring makes it fairly easy to set up but you should be aware of other impacts to your system for instances it is possible that running tasks asynchronous may slow down your request based on behavior of thread pools and be aware container threading impacts as well as having enough database connections.

Thanks for joining in today's level up, have a great day!