아키텍처와 함께

블로그 이미지
by gregorio
  • Total hit
  • Today hit
  • Yesterday hit

Spring framework Controller에서는 일반적으로 동기 방식으로 서비스를 호출한다.

그러나 요청된 파라미터의 값에 따라 동시에 여러 서비스를 호출하여 결과를 받을 때 순차적으로 서비스를 호출하지 않고  비동기로 서비스를 호출하여 결과를 Return 받으면 서비스의 수행 성능을 향상 시킬 수 있다.


Controller에서 비동기적으로 서비스를 호출하기 위해서는 Java 1.5에서부터 지원하는 Future 기능을 사용하고, 응답을 받을 경우 호출되는 서비스에서  AsyncResult를 이용하여 결과를 Return할 수 있다.


Funcure 기능과 AsyncResult를 이용하기 위해서는 task관련 Bean을 다음과 같이 정의한다.


■ context-baen.xml

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:p="http://www.springframework.org/schema/p"

        xmlns:context="http://www.springframework.org/schema/context"

        xmlns:task="http://www.springframework.org/schema/task"

        xmlns:mvc="http://www.springframework.org/schema/mvc"

        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd

                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd

                http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd

                http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">


<task:annotation-driven  executor="taskExecutor"/>

<!-- mvc:annotation-driven>

<mvc:async-support default-timeout="2500" task-executor="taskExecutor">

<mvc:callable-interceptors>

<bean class="org.springframework.web.servlet.config.MvcNamespaceTests.TestCallableProcessingInterceptor" />

</mvc:callable-interceptors>

<mvc:deferred-result-interceptors>

<bean class="org.springframework.web.servlet.config.MvcNamespaceTests.TestDeferredResultProcessingInterceptor" />

</mvc:deferred-result-interceptors>

</mvc:async-support>

</mvc:annotation-driven-->

<!-- Async Task -->

<!-- task:annotation-driven scheduler="scheduler" executor="taskExecutor"/>

<task:scheduler id="scheduler" pool-size="10"/-->

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">

    <property name="corePoolSize" value="100"/>

    <property name="maxPoolSize" value="100"/>

    <property name="queueCapacity" value="2000000000"/>

    <property name="keepAliveSeconds" value="120"/>

</bean>  

</beans> 

비동기적으로 서비스를 호출하기 위해 <task:annotation-drive>을 설정하고, executor를 정의하여 Pool size와 Capacity를 정의한다.



다음은 비동기적으로 실행할 서비스를 생성한다.


■AsyncServiceImpl.java

import org.springframework.scheduling.annotation.Async;

import org.springframework.scheduling.annotation.AsyncResult;

import org.springframework.stereotype.Service;


@Service("asyncService")

public class AsyncServiceImpl extends BaseService implements AsyncService{


@Async

@Override

public Future<List<Map<String, Object>>> selectUserListAsync(NexacroMapDTO dto) throws Exception {

LOGGER.info("selectUserListAsync Start");

Map<String, Object> searchMap = getDataSet(dto, "searchMap");

Thread.sleep(2000);

List<Map<String, Object>> resultList = getMapper("sbMapper").selectList("user.selUser", searchMap);

return new AsyncResult<List<Map<String, Object>>>(resultList);

}


@Async

@Override

public Future<List<Map<String, Object>>> selectUserListAsync1(NexacroMapDTO dto) throws Exception {

LOGGER.info("selectUserListAsync1 Start");

Map<String, Object> searchMap = getDataSet(dto, "searchMap");

List<Map<String, Object>> resultList = getMapper("sbMapper").selectList("user.selUser", searchMap);

LOGGER.info("selectUserListAsync1 End");

return new AsyncResult<List<Map<String, Object>>>(resultList);

}


}


Service 메소드에 @Async Annotation을 설정하여 해당 메소드가 비동기적 수행을 정의한다.

@Async는 public 메소드에만 정의되고, 동일한 Class에서는 @Async 메소드를 호출할 수 없다. 즉 외부의  Class에서 @Async 메소드를 호출한다.


@Async로 정의된 메소드는 Return Type이 Future<?>이고 결과는 AsyncResult를 생성하여 결과를 Return한다.


다음은 서비스를 호출하는 Controller를 생성한다.


■AsyncController


@RequestMapping(value = "/sample/selectUserAsync.do")

public ModelAndView selectUserAsync(NexacroMapDTO dto) throws Exception {

ModelAndView modelAndView = NexacroUtil.getModelAndView(dto);


Future<List<Map<String,Object>>> future1 = asyncService.selectUserListAsync(dto);


Future<List<Map<String,Object>>> future2 = asyncService.selectUserListAsync1(dto);


// List<Map<String,Object>> resultList1 = future1.get();

// List<Map<String,Object>> resultList2 = future2.get();


               modelAndView.addObject("list1", resultList1);

               modelAndView.addObject("list2", resultList2);

return modelAndView;

}

 


Controller에서 Future에 서비스의 Async Method를 호출한 후 결과를 받기 위해 생성된 future의 get Method를 호출하여 결과를 받는다.


■ 수행결과


  INFO | createCompositeTransaction ( 300000 ): created new ROOT transaction with id 192.168.0.59.tm0000100063

 INFO | createCompositeTransaction ( 300000 ): created new ROOT transaction with id 192.168.0.59.tm0000200063

 INFO | selectUserListAsync1 Start

 INFO | selectUserListAsync Start

 INFO | AtomikosDataSoureBean 'xaSBDataSource': getConnection ( null )...

 INFO | AtomikosDataSoureBean 'xaSBDataSource': init...

 INFO | atomikos connection proxy for oracle.jdbc.driver.LogicalConnection@1c182f16: calling getMetaData...

 INFO | 1. Connection opened

 INFO | 1. Connection.new Connection returned 

 INFO | atomikos connection proxy for oracle.jdbc.driver.LogicalConnection@1c182f16: calling getAutoCommit...

 INFO | 1. Connection.getAutoCommit() returned true

 INFO | addParticipant ( XAResourceTransaction: 3139322E3136382E302E35392E746D30303030313030303633:3139322E3136382E302E35392E746D31 ) for transaction 192.168.0.59.tm0000100063

 INFO | XAResource.start ( 3139322E3136382E302E35392E746D30303030313030303633:3139322E3136382E302E35392E746D31 , XAResource.TMNOFLAGS ) on resource xaSBDataSource represented by XAResource instance oracle.jdbc.driver.T4CXAResource@5c7c0881

 INFO | registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@48d3cd82 ) for transaction 192.168.0.59.tm0000100063

 INFO | atomikos connection proxy for oracle.jdbc.driver.LogicalConnection@1c182f16: calling prepareStatement(SELECT

ID,

NAME,

PASSWORD,

DESCRIPTION,

USE_YN,

REG_USER

FROM TB_USER

WHERE 1 = 1)...


결과를 확인하면 로그에 selectUserListAsync1,과 selectUserListAsync 출력됨을 확인할 수 있으며, 각각의 서비스가 순차적으로 호출되지 않고 병렬로 호출되었음을 확인할 수 있다.

 

AND

ARTICLE CATEGORY

분류 전체보기 (56)
Spring Framrwork (33)
Linux (1)
APM (1)
Java (8)
python (0)
ant (1)
chart (1)
OS (1)
tomcat (1)
apache (1)
database (0)

RECENT ARTICLE

RECENT COMMENT

CALENDAR

«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

ARCHIVE

LINK