Updated:

1. 개요

게시판에서 내가 쓴 글에 댓글이 달린 경우 나에게 알림이 오도록 구현한다고 가정해보자. 1) 댓글 작성 2) 알림 두 가지 기능으로 나눠서 생각해 볼 수 있는데, 알림 기능은 부가적인 기능이므로 댓글 작성 기능에 영향을 주면 안 된다. 예를 들어 댓글 작성 후 알림 처리가 지연되는 경우 댓글작성 자체를 지연하는 것이 아니라, 댓글 작성은 완료시키고 다른 Thread에서 알림을 처리할 수 있을 것이다. 이럴 때 활용 가능한 것이 비동기인데, 스프링에서는 @Async Annotation을 이용하여 간단하게 비동기 처리를 할 수 있다.

2. 개발 환경

  • Java 11

  • Spring Boot 2.5.3

3. AsyncConfigurer 구현

@EnableAsync Annotation을 추가하기만 해도 비동기 처리가 가능하지만, 이 경우 요청마다 매번 새로운 Thread를 생성하는 Default 설정이 적용된다. 따라서 Pool에 정해진 개수만큼 Thread를 미리 생성해놓고, 필요할 때마다 가져가서 사용할 수 있도록 AsyncConfigurer 인터페이스의 구현이 필요하다.

[AsyncConfig.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        int processors = Runtime.getRuntime().availableProcessors();
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(processors);
        executor.setMaxPoolSize(processors * 2);
        executor.setQueueCapacity(50);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("AsyncExecutor-");
        executor.initialize();
        return executor;
    }
}

Line 2 : 스프링의 비동기 기능을 활성화 하여 Async Annotation 감지

Line 7 : 본인 PC의 Processor 개수를 얻어옴

Line 8 : Thread Pool을 편리하게 관리해주는 클래스

Line 9 : 기본 Thread 개수

Line 10 : 최대 Thread 개수 (Queue가 가득 찬 이후 MaxPoolSize만큼 생성)

Line 11 : 대기를 위한 Queue의 크기

Line 12 : Thread 재사용 시간

Line 13 : Thread 이름의 Prefix

Line 14 : ThreadPoolExecutor 생성

4. Async Method 구현

[TestService.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Slf4j
@Service
public class TestService {

    @Async
    public void asyncMethod() {
        log.info("----- Async Start -----");
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("----- Async End -----");
    }
}

Line 5 : 해당 메서드를 비동기로 사용하기 위한 @Async Annotation 추가

5. 테스트

[TestController.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Slf4j
@RestController
@RequiredArgsConstructor
public class TestController {

    private final TestService testService;
    private final ApplicationEventPublisher eventPublisher;

    @GetMapping("/asyncTest")
    public void asyncTest() {
        log.info("----- Test Start -----");
        testService.asyncMethod();
        log.info("----- Test End -----");
    }
}

[테스트 결과]

의도했던 것처럼 다른 Thread에서 비동기로 처리되는 것을 확인할 수 있다.

Updated:

Leave a comment