오늘은 저번에 썻던 스레드 관련 글에 이어서 실제로 스레드 개수를 늘려나가며 테스트 하였던 과정을 소개하려고 한다!
2024.11.13 - [notice-crawler] - [교내 공지사항 알림 서비스] 개발일기 6편 [스레드를 이용한 성능 최적화]
원래 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개까지만 쓰는것으로 최종 결론을 짓게 되었다!
외부 서버를 사용해서 테스트를 하는 경우에는 실제 서비스 환경과 동일하게 테스트 하는 것의 중요성을 체감하는 하루였다..
'notice-crawler' 카테고리의 다른 글
[교내 공지사항 알림 서비스] 개발일기 8편 [서비스 페이지 소개] (1) | 2024.11.15 |
---|---|
[교내 공지사항 알림 서비스] 개발일기 13편 [지수 백오프 적용] (1) | 2024.11.14 |
[교내 공지사항 알림 서비스] 개발일기 7편 [서버 배포과정] (1) | 2024.11.14 |
[교내 공지사항 알림 서비스] 개발일기 6편 [스레드를 이용한 성능 최적화] (0) | 2024.11.13 |
[교내 공지사항 알림 서비스] 개발일기 5편 [서비스 개편] (0) | 2024.11.12 |