Async 비동기 프로젝트에 적용하기

supway·2023년 4월 5일
0

Async

목록 보기
1/1
post-thumbnail

이 게시글은 비동기 프로그래밍에 대해 공부하고 적용한 경험에 대한 회고록을 작성했습니다.

비동기 프로그래밍이란?

⇒ Async 한 통신 으로 Main Thread가 Task를 처리하는 것이 아니라 Sub Thread에게 Task를 위임하는 행위


Spring에서의 비동기 프로그래밍

=> Spring에서 비동기 프로그래밍을 하기 위해서는 ThreadPool을 정의할 필요가 있다.


ThreadPool 생성이 필요한 이유

  • 비동기는 Main Thread가 아닌 Sub Thread에서 작업이 진행
  • Java에서는 ThreadPool을 생성하여 Async 처리를 하기 때문

ThreadPool 생성 옵션

  1. CorePoolSize ⇒ 최소 몇개까지의 쓰레드를 가지고 있을 것인가
  2. MaxPoolSize ⇒ 최대 몇개까지 쓰레드 수를 설정
  3. WorkQueue ⇒ 먼저 들어온 작업부터 처리하기 위함 (WorkQueue 라는 곳에 담아두고 차례대로 작업을 가져옴 )
  4. KeepAlive TIme ⇒ 코어풀 사이즈보다 많은 쓰레드를 가졌을 때 반환을 해야하는데 내가 정한 시간만큼 이 쓰레드들이 일을 하지 않으면 반납 하겠다라는 옵션

순서

  1. CorePoolSize 만큼 쓰레드를 생성
  2. Queue에 Task를 담고, Queue가 다 차면
  3. MaximumPoolSize만큼 새로운 쓰레드를 생성

Thread Pool 생성시 주의해야할 부분

  1. CorePoolSize를 너무 크게 설정할 경우 ⇒ 사용되지 않는 쓰레드들이 있을 수 있어서 자원이 낭비됨
  2. 2가지 Exception
    • IllegalArgumentException
      • corePoolSize<0
      • keepAliveTime<0
      • maximumPoolSize ≤0
      • maximumPoolSize < corePoolSize
    • NullPointerException
      • WorkQueue가 null인 경우

Thread Pool 정리

  • CorePoolSize

if ( Thread< CorePoolSize ) 
	new Thread 생성
if ( Thread> CorePoolSize )
	Queue 요청 추가 
  • MaxPoolSize
if ( Queue Full && Thread< MaxPoolSize )
	new Thread 생성
if ( Queue Full && Thread> MaxPoolSize )
	요청 거절 

Spring에서 Async 동작원리

Spring 프레임워크 에서 비동기 프로그래밍을 하기 위해서는 Spring 프레임워크가 필요함

Async하게 동작하기 위해서 빈을 프록시 객채로 맵핑하고 프록시 객체를 리턴해줌


하게 된 배경

현재 개발하는 어플리케이션에서 사용자가 새로고침을 눌렀을 때, 깃허브 커밋 수를 갱신해주고, 해당 유저의 커밋수가 목표치에 도달했는지를 확인하고 다른 캐릭터를 부여해주는 API를 개발했다.

어플리케이션 특정상 여러 사용자가 동시에 누를 경우에도 대비를 해야하는데, 많은 유저들이 새로고침을 동시에 누른다면 동기 프로그래밍으로는 처리하는데 시간이 많이 걸릴꺼 같다는 생각이 들었다.

그래서 이번에 해당 로직들에 비동기 프로그래밍을 하는 방식으로 리팩토링을 해보고 성능이 얼마나 달라질까 궁금점이 생겨 Apache Jmeter을 사용하여 동기 프로그래밍과 비동기 프로그래밍의 성능테스트를 진행했다. 결과는 놀라웠다.


리팩토링 과정

먼저 비동기 프로그래밍을 하기위해서 @Async 어노테이션을 사용했다. @Async을 사용하면 매우 간단하게 비동기 프로그래밍을 구현할 수 있었다.

@Async

@Async Annotation은 Spring에서 제공하는 Thread Pool을 활용하는 비동기 메소드 지원 Annotation이다.

Async을 사용하기 위해서는 @EnableAsync 설정을 해줘야 한다.

@EnableAsync
@Configuration
public class AsyncConfig {
}

그 다음 ThreadPool 설정을 해주고

@Configuration
public class AppConfig {

    @Bean(name = "defaultTaskExecutor",destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor defaultTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(200);
        executor.setMaxPoolSize(300);
        return executor;
    }
}

이렇게 원하는 메서드에 @Async 어노테이션을 설정해주면 끝난다!


각 메서드에 Thread 이름을 출력하게 하고


해당 API를 호출하게 되면

이런식으로 비동기 처리가 되게 된다!

Apache Jmeter 성능테스트

Apache Jmeter 성능테스트를 이용해서 간단하게 성능테스트를 진행해봤다.

결과는
동기 프로그래밍 했을 때

비동기 프로그래밍 했을 떄

처리량은 일정 시간 동안 처리한 요청의 수로 동기 프로그래밍을 했을 때보다 비동기 프로그래밍을 했을 때 약 2배 정도 차이 나는 것을 볼 수 있다!

느낀점

이번에 기존에 있던 코드를 리팩토링하면서 비동기 프로그래밍의 효과를 직접 체감할 수 있었다. @Async 어노테이션을 이용해 코드를 비동기적으로 실행시키는 것으로 처리량이 2배 가까이 증가한 결과를 확인할 수 있었다. 이를 통해 비동기 프로그래밍의 중요성과 활용 가능성에 대해 새롭게 깨닫게 되다. 하지만 비동기 프로그래밍 자체가 자원이 들기 때문에 무조건적인 비동기 프로그래밍은 피해야 한다고 한다. 특정 상황에서 적절하게 사용을 또 해봐야 겠다.

처음으로 Jmeter를 이용해서 성능테스트도 진행했는데, 바로바로 직관적으로 볼 수 있다는 점이 좋았고, 앞으로 성능 개선시켜 유의미한 결과를 얻어 낼 수 있게 하고 싶다.

Reference
패스트캠퍼스 10개 프로젝트로 완성하는 백엔드 웹개발(Java/Spring) 초격차 패키지 Online. - IT 서비스 회사에서 사용하는 진짜 프로젝트 맛보기 강의

profile
개발잘하고싶은사람

0개의 댓글