점프 투 스프링부트 비밀번호 찾기 개선(스레드풀, DynamicUpdate) + @Async 이용 비동기 메서드 정의

박철현·2023년 12월 2일
0

점프투스프링부트

목록 보기
13/14

점프 투 스프링부트의 비밀번호 찾기, 변경 기능을 개선하였습니다.

개선 1 : 변경되는 속성만 update 쿼리 발생하도록 수정

  • 기존 : 비밀번호 변경 시 사용자 이름, 이메일 등 모든 속성값을 포함한 update 쿼리 발생
  • 변경 : password만 update하는 쿼리 발생하도록 수정
    image
  • Entity에 @DynamicUpdate 어노테이션을 추가하여 변경되는 속성만 update 쿼리 발생하도록 수정하였습니다.

개선 2 : 이메일 발송 비동기 메서드를 스레드풀을 사용하는 방식으로 수정

기존 방식

SpringBootApplication에서 @EnableAsync 어노테이션 사용

@SpringBootApplication
@EnableJpaAuditing
@EnableAsync // 비동기 기능 활성화
public class SnsFeedServiceApplication {

이후 @Async 명시만 하면 비동기 방식의 메서드 실행가능

@Async // 비동기
	public void sendEmail(String email, String userName, String tempCode) {

기존 방식의 단점

  • 위와 같이 간단하게 사용하면 Thread Executor로 SimpleAsyncTaskExecutor 사용
    • **각 작업마다 새로운 스레드를 생성**하고 비동기방식으로 동작
    • concurrencyLimit 프로퍼티를 이용해 지정한 수 보다 요청이 넘어설 경우 제한할 수 있으나 설정하지 않으면 디폴트로 unlimit
    • 스레드를 재사용 하지 않음!(스레드 풀 방식이 아님)
      • 매 요청마다 새로운 스레드를 생성
    • 스레드 풀 방식 설정(TaskExecutor) 권장!

비동기 동작 방식 flow

  • @Async 어노테이션을 기반으로 Spring AOP가 적용된 메서드여야 비동기 방식으로 동작
    • 스레드를 생성 or 스레드 풀에서 스레드를 가져오는 등의 공통 로직이 프록시 객체로 되며 추가됨
    • @Async 어노테이션의 경우 method의 접근 지정자 private 사용 불가
  • 따라서 메서드를 내부 호출로 호출할 시 비동기 방식이 불가능
    • 이 경우는 동기 방식으로 동작
  • [영상후기] [10분 테코톡] 🌕제이의 Spring AOP
    • AOP : 특정 로직을 공통 모듈로 만들어 여러 코드에서 공유하여 사용하는 프로그래밍 패러다임
      • 기능을 모듈화하고 필요한 곳에서 재사용가능
      • 출처 : 뤼튼

프로젝트 개선 점

  • ThreadPoolTaskExecutor를 설정해서 스레드풀을 생성했습니다.
    • 동작 내용
      • core 사이즈 만큼 일 처리
      • 기본 스레드가 처리할 수 있는 작업량 넘어설 경우 큐에서 대기
      • 큐의 크기가 넘친다면 MaxSize 만큼 스레드 추가 생성
      • 5개로 작업 -> 20개인 큐에서 대기 -> 넘침 -> 30개로 늘림
      • 30개로 눌렸지만, 다시 또 큐가 넘친다 RejectExecutionException 발생
        • CallerRunsPolicy 처리 (요청한 Caller Thread에서 직접 처리, 즉 기존 처럼 동기방식)
@Configuration
@EnableAsync
public class AsyncConfig {
	// core 사이즈 만큼 일 처리
	// 기본 스레드가 처리할 수 있는 작업량 넘어설 경우 큐에서 대기
	// 큐의 크기가 넘친다면 MaxSize 만큼 스레드 추가 생성
	// -----------------------예시----------------------
	// 5개로 작업 -> 20개인 큐에서 대기 -> 넘침 -> 30개로 늘림
	// 큐에서 요청이 넘긴다면 RejectExecutionException 발생 -> CallerRunsPolicy 처리
	// 요청한 Caller Thread에서 직접 처리

	@Bean(name = "taskExecutor1")
	public ThreadPoolTaskExecutor executor() {
		ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
		taskExecutor.setCorePoolSize(5);  // 스레드 풀에 속한 기본 스레드 개수
		taskExecutor.setQueueCapacity(20);  // 이벤트 대기 큐 크기
		taskExecutor.setMaxPoolSize(30); // 최대 스레드 개수
		taskExecutor.setThreadNamePrefix("Executor-");
		// ThreadPoolExecutor.CallerRunsPolicy()는 RejectedExecutionHandler 인터페이스의 구현체
		// 작업 큐가 가득 차서 더 이상 새로운 작업을 받아들일 수 없을 때의 정책을 정의합니다.
		// 이 정책에 따르면, 작업 큐가 가득 찼을 때 새로 들어온 작업은 작업을 요청한 쓰레드에서 직접 처리
		// 밑에 구현체 때문에 5개 스레드 꽉참 -> 큐 대기 -> 큐 꽉참 -> 30개 쓰레드 활용 -> 그래도 큐 꽉참 일때
		// 예외가 발생하는데, 이때 예외 처리를 담당한다. 예외 시 이 작업을 요청한 스레드에서 직접 처리하도록
		taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		taskExecutor.setWaitForTasksToCompleteOnShutdown(true); // shutdown시 queue에 남아있는 모든 작업이 완료된 후 shutdown!
		taskExecutor.setAwaitTerminationSeconds(60);	// shutdown 최대 60초 대기
		return taskExecutor;
	}
}
  • @Async 어노테이션에 Bean으로 등록된 Task 이름 명시 -> 해당 name의 Task를 스레드풀로 사용하겠다는 의미
	@Async("taskExecutor1")
	public void sendEmail(String email, String userName, String tempPW) {
		// 테스트를 위해 @test.com인 이메일은 발송하지 않음
		if (email.endsWith("@test.com"))
			return;

		SimpleMailMessage message = new SimpleMailMessage();
		message.setTo(email);
		message.setFrom(ADMIN_ADDRESS);
		message.setSubject(userName + "님의 임시비밀번호 안내 메일입니다.");
		message.setText("안녕하세요 " + userName + "님의 임시 비밀번호는 [" + tempPW + "] 입니다.");

		mailSender.send(message);
}
  • 비동기 기능 활성화 main에도 해주기
@SpringBootApplication
@EnableJpaAuditing // @EntityListeners(AuditingEntityListener.class) 가 작동하도록 허용
@EnableAsync // 비동기 기능 활성화
public class SpringAdditionalApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringAdditionalApplication.class, args);
	}
}

image

  • 스레드명 Executor- 정상 동작 확인 ㅎㅎ

기대효과

  • 스레드풀 활용하여 스레드의 생성과 삭제에 대한 비용 감소
  • 동시에 실행할 수 있는 스레드의 최대 개수 제한
    • 과도한 스레드 생성으로 인한 시스템 과부하 방지

출처 및 전체 코드 확인

profile
비슷한 어려움을 겪는 누군가에게 도움이 되길

0개의 댓글