@Async를 이용한 비동기 처리 구현

itonse·2024년 1월 11일
0

Spring

목록 보기
7/8

이미지 출처

Spring Framework에서는 @Async 어노테이션을 사용하여 간단하게 비동기 처리를 구현할 수 있으며, 순서는 다음과 같습니다.
  1. 비동기 실행 활성화
  • @EnableAsync 어노테이션을 사용하여 Spring의 비동기 실행 기능을 활성화합니다.
  • 이 어노테이션은 보통 메인 애플리케이션 클래스@Configuration 클래스에 추가됩니다.
@Configuration
@EnableAsync   // 비동기 처리 기능 활성화
public class AsyncConfig { 

    ... // 비동기 작업을 위한 스레드 풀 구성
}
  1. 비동기 메서드 정의
  • 비동기로 실행할 메서드를 정의하고 @Async 어노테이션을 추가합니다.
  • 이 메서드는 별도의 스레드에서 비동기적으로 실행됩니다.
@Service
@RequiredArgsConstructor
public class DataInitService {

    @Async   // 어노테이션 추가
    public void createMemberAndPost(int memberIndex) {
        ... // 데이터 생성 로직
    }
}
  1. 비동기 메서드 사용
  • 비동기 메서드를 호출하는 부분에서는 해당 메서드가 별도의 스레드에서 실행됩니다.
@Transactional
public void generateTestData() {
    for (int i = 0; i < TOTAL_MEMBERS; i++) {
        // 비동기 메서드 호출
        dataInitService.createMemberAndPost(i);
    }
}

적용

기존 generateTestData() 코드

@Transactional
public void generateTestData() {
    Random random = new Random();

    // Member 객체 생성
    for (int i = 1; i <= TOTAL_MEMBERS; i++) {
        Member member = Member.builder()
                .username("user" + memberIndex)
                .password(passwordEncoder.encode("password" + memberIndex))
                .email("user" + memberIndex + "@example.com")
                .verified(true)
                .paid(memberIndex % 2 == 0)   // 유료회원과 일반회원의 비율은 1:1 
                .build();

        memberService.save(member);

        // Post 객체 생성 및 저장
        boolean postPaid = member.isPaid();  // 멤버십 회원인 경우 유료글만 작성
            int postNumber = (memberIndex - 1) * POSTS_PER_MEMBER + j + 1;

            Post post = Post.builder()
                    .title("Post Title " + postNumber)
                    .body("Post Body " + postNumber)
                    .published(random.nextBoolean())   // 공개글 여부는 랜덤
                    .paid(postPaid)
                    .author(member)
                    .build();

            postService.save(post);
    }
}

1. AsyncConfig에서 스레드 풀을 구성하고, 비동기 처리 기능 활성화

@Configuration
@EnableAsync   // 비동기 처리 기능 활성화
public class AsyncConfig {    // 비동기 작업을 위한 스레드 풀 구성

    @Bean(name = "threadPoolTaskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);   // 항상 실행 중인 스레드의 최소 수 (CPU의 코어 수를 기반으로 설정)
        executor.setMaxPoolSize(15);    // 동시에 실행될 수 있는 최대 스레드 수 (코어 수의 2 ~ 4배가 일반적)
        executor.setQueueCapacity(500);   // 스레드 풀에서 처리될 때까지 대기하는 작업의 최대 수
        executor.setThreadNamePrefix("Executor-");    // 의미 있는 이름 (디버깅 및 로깅 목적)

        executor.initialize();    // 스레드 풀을 초기화 및 사용 준비

        return executor;
    }
}

2. @Async 어노테이션을 사용하기 위해 비동기로 실행하고자 하는 작업을 별도의 메서드로 분리

  1. 비동기 메서드 정의: DataInitService 생성
@Service
@RequiredArgsConstructor
public class DataInitService {
    private static final int POSTS_PER_MEMBER = 1;   // 한 멤버가 작성할 글의 수

    private final MemberService memberService;
    private final PostService postService;
    private final PasswordEncoder passwordEncoder;

    @Async("threadPoolTaskExecutor")   // 비동기 실행을 위해 해당 이름의 스레드 풀 사용
    public void createMemberAndPost(int memberIndex) {
        Random random = new Random();

        // Member 객체 생성 및 저장
        Member member = Member.builder()
                .username("user" + memberIndex)
                .password(passwordEncoder.encode("password" + memberIndex))
                .email("user" + memberIndex + "@example.com")
                .verified(true)
                .paid(memberIndex % 2 == 0)    // 멤버십 회원과 일반회원의 비율은 1:1
                .build();

        memberService.save(member);

        // Post 객체 생성 및 저장
        for (int j = 0; j < POSTS_PER_MEMBER; j++) {
            boolean postPaid = member.isPaid(); // 멤버십 회원인 경우 유료글만 작성
            int postNumber = (memberIndex - 1) * POSTS_PER_MEMBER + j + 1;

            Post post = Post.builder()
                    .title("Post Title " + postNumber)
                    .body("Post Body " + postNumber)
                    .published(random.nextBoolean())   // 공개글 여부는 랜덤
                    .paid(postPaid)
                    .author(member)
                    .build();

            postService.save(post);
        }
    }
}
  1. 비동기 메서드 호출
@Configuration
@RequiredArgsConstructor
public class NotProd {

    private final DataInitService dataInitService;
    
    ...
    
    @Transactional
    public void generateTestData() {
        for (int i = 1; i <= TOTAL_MEMBERS; i++) {
        	// 비동기 메서드 호출 (데이터 처리를 위한 파라미터 전달)
            dataInitService.createMemberAndPost(i);  
        }
    }


회원 200명, 게시글 200개를 생성하는데 걸리는 시간

기존: 40초

비동기 처리 적용 후: 17초

*주의 사항

데이터 처리를 할 때 멀티스레딩을 사용하면 성능을 향상시킬 수 있지만, 동시성 문제를 일으킬 수가 있으므로 주의가 필요합니다.


ref.
https://akku-dev.tistory.com/89
https://cano721.tistory.com/208
https://dkswnkk.tistory.com/706

0개의 댓글

관련 채용 정보