Monday, November 4, 2013

Enable Async Request Processing for Java Spring

Serlvet 3 supports asynchronous request processing which allows the requested operation to be performed on a separate thread, freeing the HTTP request memory.

In Spring, an allocated amount of memory is allocated for request processing. If you are running a long operation (such as uploading a large file, batch mailing, big data analysis), the memory for that request would be blocked until the operation is finished.

A better way of approaching this would be to return the request right away and let the long operation to run in another thread.

In Spring 3.x, it is relatively easy with the @Async support.

Before I dive down into the tutorial, here's my specs:
  • ubuntu 12.04
  • large EC2 instance
  • OpenJDK 1.7
  • Maven 3.0.4
  • Spring 3.2.4
  • CXFServlet 2.5.3 with JAX-RS (This is for Rest API)
This post will be about how to enable @Async in your spring project.


Install the latest version of Spring

At the time of this writing, the stable version is 3.2.4. The following shows how to add the maven dependency in pom.xml

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
</dependencies>


Use Serlvet 3.0 namespace

In web.xml, make sure you use the XML nameplace for version 3.0.

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>
 


Add Async Support in entry servlets in web.xml

If you are writing a normal Spring MVC Web app, you will need to add async-supported in the dispatcher serlvet below. My project only uses the Rest API, so I put the async-supported xml tag in the CXFServlet.

<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>


Enable Async in application context

In applicationContext.xml, enable executor and scheduler. If you don't know where is the applicationContext.xml, find it from web.xml's contextConfigLocation context-param field.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
default-lazy-init="true">
<!-- async support -->
<task:annotation-driven executor="myExecutorService" scheduler="mySchedulerService" />
<task:executor id="myExecutorService" pool-size="5-10" queue-capacity="10" />
<task:scheduler id="mySchedulerService" pool-size="10" />
</beans>
view raw bean_async.xml hosted with ❤ by GitHub

Make sure you have the task namespace as above.


Annotate a method as @Async

Below is a quick test of the @Async method. Notice the @Async annotation below.

public interface MyService {
Future<String> testAsync();
}
@Service
@Transactional
public class MyServiceImpl implements MyService {
@Async
@Override
public Future<String> testAsync() {
String hello = "world";
String threadName = Thread.currentThread().getName();
System.out.println(" " + threadName + " beginning work");
try {
Thread.sleep(10000); // simulates work
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(" " + threadName + " completed work");
return new AsyncResult<String>(hello);
}
}
public interface MyApiService {
String testAsync() throws Exception;
}
@Path("/api")
@Service("srvMyApi")
public class MyApiServiceImpt implements MyApiService {
@Override
@GET
@Path("/testAsync")
@Produces(MediaType.APPLICATION_JSON)
public String testAsync() throws Exception {
Future<String> future = bookService.testAsync();
HttpServletRequest req = context.getHttpServletRequest();
return "called Async";
}
}

Try taking out the @Async annotation and execute the program. You should be able to observe the difference.

2 comments:

  1. Interesting topic for a blog. I have been searching the Internet for fun and came upon your website. Fabulous post. Thanks a ton for sharing your knowledge! It is great to see that some people still put in an effort into managing their websites. I'll be sure to check back again real soon.
    Minecraft Server List

    ReplyDelete
  2. Intresting information app spring java i love iot and wants to get more info about it

    ReplyDelete