@Async 애노테이션의 사용 Spring Framework

@Async 애노테이션은 이름처럼 비동기 처리를 가능하게 하는 역할을 한다. 주기적인 메일 전송, 푸시 전송, 메세지 전송처럼 각각의 작업은 완전히 독립되어 있지만 주기적으로 많은 양을 처리해야 하는 배치작업에 알맞는 기능(이라고 생각한다. 아님 말고) APNS/GCM 푸시 전송에 적용해 보았는데 그 결과도 괜찮았기 때문에 내용을 공유해 본다.

1. @Async 애노테이션을 적용할 메서드를 작성한다. 이 메서드는 비동기로 처리되므로 동기 처리가 필요한 메서드 및 기능을 사용하면 안된다. 빈 객체로 사용하는 경우 dao빈등을 Autowired등으로 DI 받을 수도 있다.

public class AsyncService
{
    @Async
    public void print(int count)
    {
        System.out.println("start:" + count);   
       
        try
        {
            Thread.sleep(5000);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
       
        System.out.println("end:" + count);
    }
}

int 숫자를 입력하면 시작으로 print를 찍고 5초 대기 후 종료를 찍고 실행을 완료하는 간단한 메서드이다. 메서드 위에 @Async 애노테이션을 사용한 것을 확인한다.

2. 컨트롤러에서 메서드를 사용하는 코드를 작성한다.

@Controller
public class IndexController
{
    @Autowired AsyncService asyncService;
   
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public void index()
    {
        for(int i = 0; i < 10; i++)
        {
            asyncService.print(i);
        }
    }
}


AsyncService를 Autowired로 가져오므로 AsyncService를 빈 객체로 띄워야 한다. 빈 정의는 일반 빈 정의와 동일하게 해주면 된다.

<bean id="asyncService" class="com.preludeb.asynctest.service.AsyncService"></bean>


이런식으로 정의 한다.


3. 비동기 처리를 담당하는 실행기와 애노테이션을 인식하는 task:annotation-driven을 추가한다. 다음은 사용한 빈 정의 XML이다.

<?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: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/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
      
 <bean id="asyncService" class="com.preludeb.asynctest.service.AsyncService"></bean>
      
 <task:executor id="asyncExecutor" pool-size="100-1000" queue-capacity="1000"  rejection-policy="ABORT" />

 <task:annotation-driven executor="asyncExecutor" />
 
</beans>

실행기 정의에서 pool-size를 100-1000으로 정의 했으므로 실행하는 스레드 풀은 최소 100개에서 최대 1000개를 유지한다. 그리고 이 풀에 실행내용을 전달하기 전에 저장하는 큐의 크기는 queue-capacity로 1000개로 정의했다. 마지막 정의는 큐에도 가득차고 스레드도 가득찬 경우 실행방식을 정의한다. 여기서는 ABORT로 정의 했으므로 예외를 발생시키고 해당 실행을 무효화 시킨다.

이 정의에 의해서 스레드가 동작하는 방식은 빈이 초기화될때 최소 100개의 스레드를 생성한다. 그리고 큐를 통해서 전달된 명령을 처리한다. 만약 큐가 1000개의 명령으로 가득차면 순차적으로 스레드를 증가시킨다. 그리고 스레드, 큐 모두 가득찬 경우에는 마지막 rejection-policy에 따라서 오류 처리를 진행한다.

4. 앞에서 작성한 컨트롤러를 통해서 해당 부분을 실행하면 다음과 같은 결과가 출력된다.


start:0
start:8
start:6
start:4
start:2
start:1
start:3
start:5
start:7
start:9
end:8
end:4
end:6
end:0
end:2
end:1
end:9
end:7
end:5
end:3


10개의 스레드가 시작된 후 5초 후에 종료 부분이 실행됨을 확인할 수 있다. 비동기와 동기처리에 대해서 비교하고 싶다면 동일한 구조에서 @Async 애노테이션을 제거하고 실행해보면 그 차이를 확인할 수 있다.

-----------------------------------------------------------------------------------------------

추가적으로 사용한 라이브러리

asm-3.3.1.jar
cglib-2.2.2.jar


덧글

댓글 입력 영역