[교내 공지사항 알림 서비스] 개발일기 9편 [스레드 부하 테스트]

오늘은 저번에 썻던 스레드 관련 글에 이어서 실제로 스레드 개수를 늘려나가며 테스트 하였던 과정을 소개하려고 한다!

2024.11.13 - [notice-crawler] - [교내 공지사항 알림 서비스] 개발일기 6편 [스레드를 이용한 성능 최적화]

 

[교내 공지사항 알림 서비스] 개발일기 6편 [스레드를 이용한 성능 최적화]

오늘은 기존 이메일 발송시간이 너무 오래 걸리는 문제를 해결하는 과정을 기록하도록 하겠다. 기존 코드에서는 30명의 사용자에게 메일을 보내는데 1분 30초정도가 걸렸다. (49:24 ~ 50:55) 메일

mythpoy.tistory.com

 

원래 Mockito 라이브러리를 통해 EmailService를 가짜 객체로 생성하여 테스트를 하고있었는데 실제 네트워크 I/O도 고려해야 했기 때문에 그냥 메일을 보내는 테스트로 진행하였다.

 

Gmail 발송 제한 완화할려고 주말에 테스트함!! ㅋㅋ

 

 

실제 사용했던 테스트 코드는 다음과 같다.

 

package io.jyp.crawler;

import io.jyp.crawler.entity.Member;
import io.jyp.crawler.service.EmailService;
import jakarta.mail.MessagingException;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@Slf4j
@SpringBootTest
class multiThreadingTestRunner {

    @Autowired
    private EmailService emailService;

    @Test
    void multiThreadingTest() {
        runTestWithThreads(10);
    }

    private void runTestWithThreads(int threadCount) {
        ExecutorService emailExecutor = Executors.newFixedThreadPool(threadCount); // 스레드 개수 설정

        // 더미 Member 데이터 생성
        List<Member> dummyMembers = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            dummyMembers.add(Member.builder()
                .email("ju" + i + "_park@naver.com")
                .noticeFlag(true)
                .noticeType("MAIN")
                .build());
        }

        long startTime = System.nanoTime();

        // CompletableFuture 리스트를 만들어 모든 작업이 완료될 때까지 기다림
        List<CompletableFuture<Void>> futures = dummyMembers.stream()
            .map(member -> CompletableFuture.runAsync(() -> {
                try {
                    emailService.sendEmail(member, "test");
                    log.info("[이메일 발송] {}", member.getEmail());
                } catch (MessagingException e) {
                    log.error("[이메일 발송 실패] {}", member.getEmail(), e);
                    throw new RuntimeException(e); // 예외를 명시적으로 던져 CompletionException의 원인을 알 수 있도록 함
                }
            }, emailExecutor))
            .toList();

        // 모든 작업이 끝날 때까지 기다림
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        long endTime = System.nanoTime();
        double executionTimeSeconds = (endTime - startTime) / 1_000_000_000.0; // 초 단위로 변환
        log.info("Thread Count: {}, Execution Time: {} seconds", threadCount, executionTimeSeconds);

        // ExecutorService 종료
        emailExecutor.shutdown();
    }
}

 

처음에는 스레드 10개를 이용해서 더미멤버 30명에게 메일 발송을 해보았다.

테스트 과정을 영상으로 찍어놨는데 티스토리는 업로드 제한이 있어서 유튜브에 게시하였다!

 

스레드 10개 테스트 영상

https://www.youtube.com/watch?v=9AImtZVkjfo

 

 

스레드 20개 테스트 영상
https://www.youtube.com/watch?v=xDTUUnSSPko

 

보다시피 20개로 테스트를 돌리니까 실패한다. 

이메일 보내는 순간의 cpu나 ram 상태를 보면 자원부족은 아니다.

디버그를 자세히 보니 너무 짧은시간에 메일을 많이 보내서 Gmail SMTP 서버쪽에서 Exception을 던진 것이었다.

Failed messages: org.eclipse.angus.mail.smtp.SMTPSendFailedException: 421-4.3.0 Temporary System Problem. Try again later (10). For more information,
421-4.3.0 go to
421 4.3.0  https://support.google.com/a/answer/3221692 d2e1a72fcca58-720bc2f189dsm5435241b3a.181 - gsmtp

org.springframework.mail.MailSendException: Failed messages: org.eclipse.angus.mail.smtp.SMTPSendFailedException: 421-4.3.0 Temporary System Problem. Try again later (10). For more information,
421-4.3.0 go to
421 4.3.0  https://support.google.com/a/answer/3221692 d2e1a72fcca58-720bc2f189dsm5435241b3a.181 - gsmtp
; message exceptions (1) are:
Failed message 1: org.eclipse.angus.mail.smtp.SMTPSendFailedException: 421-4.3.0 Temporary System Problem. Try again later (10). For more information,
421-4.3.0 go to
421 4.3.0  https://support.google.com/a/answer/3221692 d2e1a72fcca58-720bc2f189dsm5435241b3a.181 - gsmtp

 

그래서 최종적으로 스레드는 최대 10개까지만 쓰는것으로 최종 결론을 짓게 되었다!

외부 서버를 사용해서 테스트를 하는 경우에는 실제 서비스 환경과 동일하게 테스트 하는 것의 중요성을 체감하는 하루였다..