반응형

비동기 처리 시 기본적으로 @Async를 사용한다.


JavaConfig기준 Spring 설정파일에서 @EnableAsync을 붙이기만 하면 바로 사용이 가능해진다.


하지만, 위 설정만으로 사용하게 되면, 기본적으로 단일 쓰레드로 동작을 하게 된다.

(ThreadPool설정을 보니 Default값이 1로 되어 있다.)


log를 @Async를 이용해서 보내게 되는 경우,

확실히 대규모 접속 서비스 같은 경우 단일쓰레드만으로는 사용이 어렵다.


로드러너를 이용한 성능 테스트에서도 목표 성능에 미치지 못했었던 적이 있다.


위 문제를 해결하기 위해, ThreadPooling을 해야 될 것 같아서,

스프링 사이트를 뒤져봤다.


참조 : http://docs.spring.io/spring/docs/4.1.x/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html


AsyncConfigurer interface를 구현하겠다고 선언하면, 아래와 같이 두 가지 메소드에 대해서 구현을 하라고 나오는데,

getAsyncExecutor()가 내가 원하던 ThreadPooling역할을 해줄 것이다.

getAsyncUncaughtExceptionHandler()를 구현해서 이용할 수 있는 에러 핸들링은 필요에 따라 사용하는걸로...


@Override
public Executor getAsyncExecutor() {
     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
     executor.setCorePoolSize(10);         // 최초에 생성할 Pool 개수
     executor.setMaxPoolSize(50);          // 몇개까지 Pool을 생성할 것인지
     executor.setQueueCapacity(100);      // Async 처리 시 Queue Size 
                                                      // (설정하지 않으면 Integer.MAX이기 때문에 성능에 문제가 발생함)
     executor.setThreadNamePrefix("LogAsync-");   // Thread 생성 시 가장 앞에 붙는 이름을 뭘로 할 것인지
     executor.initialize();
     return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
}



참고로 위 설정에서 사용하는, ThreadPoolTaskExecutor가 생각보다 목표 성능에 미치지 않고 불안정하다면, 

ConcurrentTaskExecutor를 이용해서 동시성에 대한 처리를 개선할 수 있다고 한다.

참조 : http://blog.outsider.ne.kr/1066 


전에 단일쓰레드 사용으로 문제가 있었으니, 

쓰레드풀링으로 다시 이용해보고 결과는 다시 포스팅해야 겠다.


[테스트 결과]

위 처럼 QueueCapacity를 100으로 잡고, 부하를 줘봤더니 일반적인 부하에서는 문제가 없었지만,

임계치까지 부하를 준 경우에 담아둘 수 있는 Queue Capacity가 작아서 Exception을 떨구며, 처리를 못하는 것들이 꽤 생겼다.


그래서, QueueCapacity를 CorePoolSize * MaxPoolSize * 2  = 1000 수치로 잡고 테스트를 해보았더니

임계치까지 부하를 줘도 모두 처리가 가능했고, CPU 사용률도 양호했다.


실무에서는, 로그를 DB에 insert하는 부분에서 사용하고 있다.

반응형
,