spring async vs sync 테스트

비동기로 동작하는게 좋은 것 같은데, 얼마나 좋을지 테스트해 보았다.

시작은 비동기가 얼마나 좋을까였지만, 결론은 사뭇다르다.


어쨋든, 비동기로 동작할 수 있게 셋팅 법과 코드를 먼저 보자.


요청 쓰레드 제한하기

비동기 활성화 시키기

비동기용 워커 쓰레드 설정하기

동기와 비동기용 코드 구현


servlet-context.xml파일에서 taskExecutor 파라미터 설명

queueCapacity에 지정한 수치만큼 큐에 쌓일 경우, 쓰레드를 생성한다.

단, maxPoolSize까지만 생성된다.


만약, 

테스트 코드에는 요청 받으면, sleep(1초)을 시키고, 쓰레드 수와 큐 길이를 1로 지정 후, 
세 번의 요청을 동시에 보내면,
첫 번째 요청은 쓰레드 1개를 사용 중일 꺼고, 

두 번째 요청은 큐에 들어가 있을 꺼고,

세 번째 요청은 오류가 발생한다.

<beans:bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <beans:property name="corePoolSize" value="1"/> <beans:property name="maxPoolSize" value="1"/> <beans:property name="queueCapacity" value="1"/> <beans:property name="keepAliveSeconds" value="120"/> </beans:bean>

asyc-timeout이 처리 시간보다 짧다면, 예외가 발생한다.

테스트 코드는 요청 받으면 1초 지연인데, 비동기 타임 아웃은 100밀리로 설정해 오류가 발생했다.

<annotation-driven> <async-support default-timeout="100" task-executor="taskExecutor"/> </annotation-driven>


테스트

5000개의 요청을 서버에 보냈다.

동기 테스트 할 때는 요청 쓰레드 150개로 설정,

비동기 테스트 할 때는 요청 쓰레드 1개, 비동기용 워커 쓰레드는 150개로 설정했다.

즉, 테스트에 사용할 쓰레드 수는 동일하게 정했다.


서버 JavaVM 힙 메모리는 1GB로 설정했다.

참고로, 요청 쓰레드 수는 server.xml에서 Excutor의 maxThreads이며,

비동기용 워커 쓰레드 수는 servlet-context.xml에서 taskExcutor에서 corePoolSize와 maxPoolSize를 조정했다.

서버는 요청 받으면, 1초 지연 시킨 후, 응답을 줬다.


비동기 테스트 결과

메모리 사용율이 비동기 보다 월등히 높고,

CPU 사용율도 초반 요청이 들어올 때가 높은 편이다.

CPU 사용율에서 처음 튀는 산은 웹 서버가 막 부팅되어 로딩과 초기화 작업의 영향이니 무시해도 좋다.

두 번째 튀는 산부터 테스트 부하이다.


동기 테스트 결과


요청 쓰레드 75, 비동기 쓰레드 75개로 수정 후,

비동기 요청과 동기 요청 각 각 50%씩 보내봤다.

예상대로 위 메모리 결과의 평균값을 보여줬다.

즉, 의미 없는 테스트였다.


결론

요청 처리 지연시간을 1초로 지정하여, 비동기와 동기 요청을 비교해 보면,

동기 요청이 CPU와 메모리 사용율이 낮아 성능이 좋다.

처리 지연시간이 동일하니 비동기를 사용하는게 득보다 독이 된 거다.

즉, 요청에 대한 처리 시간이 비슷하다면, 동기 방식이 좋다.


그렇다면 비동기는 의미가 없나? 아니다.

비동기 또는 동기 테스트 중, 간단하게 응답이 올까하고 에코용으로 만든 요청을 보내봤지만, 

쓰레드가 모두 바빠서, 테스트로 보낸 건이 끝날 쯤에야 응답을 받았다.


위 테스트의 요점은 

어떤 한쪽이 좋다가 아니라,

빠른 처리가 예상되는 요청은 동기로, 

느린 처리는 비동기로 구분해 사용하는 거였다.

구분해 사용하면, 느린 작업이 모든 쓰레들 차지해 빠른 작업을 처리할 수 없는 상황을 막을 수 있다.