Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(server): 메일전송을 비동기로 처리한다. #513

Merged
merged 6 commits into from Oct 27, 2021

Conversation

DWL5
Copy link
Collaborator

@DWL5 DWL5 commented Oct 24, 2021

메일전송 비동기로 처리하도록 Async 추가했어!
Async설정을 해주는 부분이 있는데, 이 부분을 일단 임의로 설정했어!
우리 서비스가 아직 그렇게 많은 요청이 들어오지 않아서 작게 잡아봤어!

비동기 테스트를 어떻게 할지 몰라서 일단 직접해보는 식으로 진행했는데, 개선 된 것 확인했어!
메일 전달 내용에도 문제가 없더라구!

PR올려볼게!

변경사항

  • AsyncConfig 추가 및 메일전송 메소드에 @async 추가

    • CorePoolSize : 기본적으로 실행을 대기하고 있는 Thread의 개수
    • MaxPoolSize : 동시 동작하는, 최대 Thread 개수
    • QueueCapacity : CorePoolSize를 초과하는 요청이 Thread 생성 요청시 해당 내용을 Queue에 저장하게 되고, 사용할 수 있는 Thread 여유 자리가 발생하면 하나씩 꺼내서 동작
    • ThreadNamePrefix : Spring이 생성하는 쓰레드의 접두사를 지정
    • 참고
  • PaymentInfo NoArgsContructor 추가

    • 이제 dev 서버 DB가 h2가 아니라서 로컬에서 돌리기가 힘들었으..
    • 그래서 Performance 환경으로 돌려서 테스트 해보았는데, Mock 결제 모듈 API를 사용하는 도중 PaymentInfo를 파싱하지 못하는 에러 발생해서 추가해줬어!
  • index.html

    • 변경된 결제 로직에 맞춰서 수정해놨어!

ps. 추가로 여러 상황에서 다른 Async 설정을 사용하고 싶을 때는 어떻게 할까 찾아봤는데 이때는 @overide말고 @qualifier를 선언해서 사용한대!

@DWL5 DWL5 requested review from Joyykim, Rok93 and Be-poz October 24, 2021 15:55
@DWL5 DWL5 self-assigned this Oct 24, 2021
@DWL5 DWL5 added feature 새로 추가될 기능 refactor 기능에 변경이 없는 코드 리팩터링 server 백엔드 관련 이슈 labels Oct 24, 2021
@DWL5 DWL5 added this to the TYF-SPRINT9 milestone Oct 24, 2021
@DWL5 DWL5 linked an issue Oct 24, 2021 that may be closed by this pull request
Copy link
Collaborator

@Rok93 Rok93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수리 고생많았어!
메일 보낼 때, 시간이 꽤 지연돼서 은근 불편했는데 반영되고나면 그 불편함이 사라지겠지? 🤔
불편함을 해소해줄 수 있는 이슈라 체감이 클 것 같네 😃

몇몇 제안 남겨놨는데, 괜찮다 싶으면 반영해줬으면 좋겠네!

@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default corePoolSize가 1이라서 이 메서드는 생략해도 괜찮을 듯!
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

응 만약 1로 두는 거면 삭제해야겠다.

executor.setCorePoolSize(1);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-mail-service");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 mail 기능 말고도 비동기로 만들 기능이 있으면 이 부분은 리팩토링 대상이라고 봐야겠네?
아 ThreadNamePrefix만 그런 줄 알았는데, 수리가 코멘트 마지막에 언급한 ps. 추가로 여러 상황에서 다른 Async 설정을 사용하고 싶을 때는 어떻게 할까 찾아봤는데 이때는 @overide말고 @qualifier를 선언해서 사용한대!

음... 개인적인 생각이지만, override말고 @Qualifier 애너테이션으로 변경하면 어떨까? 🤔
추후 비동기 기능이 생길지 안생길지는 조금 의문이지만, 그때 작업하는 작업자가 수리의 마지막 코멘트를 기억한다면 다행이지만 아닌 경우에는 또 다시 관련 자료(= Qualified 관련)를 찾는데 시간을 보낼 것 같아서 🤔
변경에 큰 어려움이 있는 것이 아니라면 첫 번째 비동기 기능을 @Qualified 형식으로 만들어 두면 다음 작업자가 작업하는데 많은 도움이 될 것 같다는 생각을 해봤어!!!

(고민해야할 점이 많다면 스킵해도 무방!!)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스는 AsyncConfig인데 쓰레드 이름이 async-mail-service인건 아닌듯!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Qualifier 로 진행한다면 어떠한 형태를 띄는지가 궁금해 ! AsyncConfigurerSupport 를 상속받는 다른 AsyncConfig 클래스가 생기는 건지 아니면, getAsyncExecutor 처럼 Executor를 반환하는 다른 메서드가 생기는건지

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
상속 안받고 그냥 이런식으로 Executor를 리턴하는 빈을 추가해주는 식도 할 수 있나본데, 수리랑 한 방식이랑 무슨 차이가 있는거쥐

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://cofs.tistory.com/319
여기보면 로키가 말한 interface implements 하면서 @Qualifier를 사용하는 예시가 나와있네
근데 @Qualifier 사용방법이 잘못됐네... 사용하는 이유가 없는 코드긴하다

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Qualifier랑 interface를 implements 하는 방식은 별개의 얘기였어!
Qualifier는 수리 코멘트에 여러 상황에서 다른 Async 설정을 사용하고 싶을 때는 어떻게 할까 찾아봤는데 이때는 @overide말고 @qualifier를 선언해서 사용한대! 부분을 참고해서 의견 냈던거야!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 응! 나도 로키가 말한 inerface implements 하면서 /// 여기 까지가 로키 의견 ! @Qualifier 는 pr 코멘트 생각하면서 말한거였어 ㅋㅋㅋㅋ 오해할 만한 문장이었다 ㅋㅋㅋ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파즈, 로키가 의논 했던 부분 qulifier하고 상속 관련은 밑에 코멘트에 정리해놨어!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조이 AsyncConfig 이긴 하지만, 추후 다른 thread 관련 설정도 해당 config에서 하지 않을까 싶어서
mail 이라는 키워드를 넣어서 분리해봤어!

@@ -22,21 +23,22 @@
@Component
@RequiredArgsConstructor
public class SmtpMailConnector {
// todo 메일 전송 시간이 김. 비동기로 해볼까?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오... 언행일치 👍👍 멋진데?

Comment on lines 31 to 32


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

불필요한 줄바꿈은 제거하면 좋을 것 같어 😃


private static final String PREFIX_SUBJECT = "[Thank You For]";

private final JavaMailSender javaMailSender;
private final TemplateEngine templateEngine;


@Async
Copy link
Collaborator

@Rok93 Rok93 Oct 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트 방법을 몰라서 직접 테스트를 못해봐서 미안해....

Baldung - @Async 관련 자료를 참고해보면 @Async를 클래스레벨에 적용하면 클래스 내에 모든 public 메서드에만 적용된다고 해!

메서드 각각에 @Async를 선언하는 것보다 SmtpMailConnector 클래스에 @Async를 선언하면 어떨까? 🤗

참고자료에서 발췌

It must be applied to public methods only. (이 외에 내용도 읽어보면 좋을 것 같네!! 이미 읽어봤을지도 모르겠다 👀)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오오 조아욘!!!!!!!!! 이거는 적용했다.


@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public class AsyncConfig extends AsyncConfigurerSupport {
public class AsyncConfig implements AsyncConfigurer {

자바 8 이후부터는 default 메서드 덕분에 AsyncConfigurer를 바로 구현해도 된다고 하는데 🤔
AsyncConfigurerSupport 구현체를 보면 AsyncConfigurer랑 차이가 없는 것을 알 수 있을꺼야!(default 제외)
별다른 이점이 없다면 최상위 클래스를 상속 or 구현하는 것이 좋다고 생각해서 이렇게 변경하면 좋을 것 같은데 어떻게 생각해? 😃

참고자료

Copy link
Collaborator

@Joyykim Joyykim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Async를 테스트해볼 순 없을까?

}).fail(function (e) {
alert("실패!")
});
await jQuery.ajax({
Copy link
Collaborator

@Joyykim Joyykim Oct 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

index.html 삭제할까 생각중인데 혹시 고친 이유가 있어? 이거 쓰나?
비동기 테스트해보려고 한거야?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

응..비동기테스트는 직접 밖에 아직 방법을 못찾아서
나도 이 index.html 삭제하고픔.

삭제하기 전에 파노, 인치에게 프론트 개발 서버 또한 배포를 하면 어떨지에 대해서 말해볼까?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그건 따로 말하고 이건 그냥 삭제해도 될거같은데? 어차피 이거 쓰느 사람없고, 지금 api 서버에도 배포되어 있어서 아주 불편해


import java.util.UUID;

@NoArgsConstructor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

없어도 되지 않아? 왜 붙였어?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
라고 하넹

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아고 저건 못봤넹

executor.setCorePoolSize(1);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-mail-service");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스는 AsyncConfig인데 쓰레드 이름이 async-mail-service인건 아닌듯!

Comment on lines 17 to 19
executor.setCorePoolSize(1);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corePoolSize, maxPoolSize, queueCapacity 는 어떤 기준으로 정했는지 궁금해

Copy link
Collaborator

@Be-poz Be-poz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수리 commit 내역 잘 봤어! 변경해야 할 점들은 이미 다른 팀원들이 다 말해준 것 같다.
image
다만, 여기 설명에서 잘못된 점이 있어서 짚고 넘어가면 좋을 것 같아서 말해.
Core/Max PoolSize 는 적혀있는 내용이 맞고,

QueueCapacity : MaxPoolSize를 초과하는 요청이 Thread 생성 요청시 해당 내용을 Queue에 저장하게 되고, 사용할 수 있는 Thread 여유 자리가 발생하면 하나씩 꺼내서 동작

이 부분이 조금 틀려. WorkingQueue 동작 방식은 먼저 CorePoolSize가 꽉 차면 해당 Queue에 집어넣게돼. 그리고 해당 Queue가 모두 꽉 차면 그때가 되어서야 MaxPoolSize 수 까지 올려가면서 스레드를 생성하게 되는거야! 좋은 예시 있는 블로그를 찾아봤는데 링크 여기 굉장히 예시가 좋다! 한 번 읽어보면 큰 도움될 것 같어!


@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우리가 지난번에 했던 http 미션에서 ExecutorService가 있고 그것을 통해서 스레드 관리를 해줄 수가 있었는데링크,
이것과 무슨 차이가 있는지 좀 찾아봤더니

Spring을 사용한다면 ThreadPoolTaskExecutor를 살펴보는 것도 좋다. 내부 구현은 ThreadPoolExecutor로 구현되어 있다. ThreadPoolExecutor 보다 조금 더 간편하며, 추가적인 return 타입도 있다.

출처 http://wonwoo.ml/index.php/post/2254

spring에서 조금 더 편하게 만든 스레드 관리 클래스였어!
image
내부적으로는 ThreadPoolExecutor를 사용하고 있었고,
image
image
workingQueue의 size도 new LinkedBlockingQueue<>(queueCapacity);를 이용하고 있는 것을 볼 수가 있는데, 저 큐는 ThreadPoolExecutor 에서도 사용하는 큐야! 스프링 별거를 다 도와주네 허허

@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나는 이거 한 5 정도는 되어야 할 것 같다고 생각해

executor.setCorePoolSize(1);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-mail-service");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Qualifier 로 진행한다면 어떠한 형태를 띄는지가 궁금해 ! AsyncConfigurerSupport 를 상속받는 다른 AsyncConfig 클래스가 생기는 건지 아니면, getAsyncExecutor 처럼 Executor를 반환하는 다른 메서드가 생기는건지

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workingQueue 에 대한 설명이 조금 틀린 것 같아서 메인 코멘트에 작성해뒀어!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오케 나도 맨 처음 PR 코멘트에 반영했으!!

@@ -22,21 +23,22 @@
@Component
@RequiredArgsConstructor
public class SmtpMailConnector {
// todo 메일 전송 시간이 김. 비동기로 해볼까?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 근데 주석 작성 조이 아님 ? ㅋㅋㅋ

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아... 왜 수리가 적은줄 알았지...? 👀

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅇㅇ나임ㅋㅋ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋㅋㅋㅋ 조이임


import java.util.UUID;

@NoArgsConstructor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
라고 하넹

@DWL5
Copy link
Collaborator Author

DWL5 commented Oct 26, 2021

나도 Async에 대해 잘 숙지하지 못하고 써서 좀 정리 해봤어!

@async에 대해

AsyncConfigurerSupport를 왜 상속

@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-mail-service");
        executor.initialize();
        return executor;
    }
}
  • 기본적으로 @EnableAsync 설정만 해주고, @async 애노테이션을 사용하면 비동기처리가 가능하다고 함.
  • 그러나 기본 설정은 SimpleAsyncTaskExecutor를 사용
  • SimpleAsyncTaskExecutor는 어떤 스레드도 재사용하지 않고, 호출마다 새로운 스레드를 시작함.
  • 그래서 AsyncConfigurerSupport를 상속해서 getAsyncExcecutor를 오버라이드해서 ThreadPoolTaskExcecutor를 사용하도록 변경

결론

  • 기본적으로 Async() 애노테이션을 쓸 경우 기본적으로 사용하게 될 Excutor를 정의해 준 것

@async를 다양한 설정으로 적용하고 싶을 때?

  • Qualifire를 사용하면 된다고 애매하게 말했던 부분에 대해서 추가 설명할게!

Qualifier

  • Bean 주입을 받아야하고,
  • Runnable 구현 후
  • 주입 받은 executor로 실행
   @Qualifier("mailExecutor")
    private final ThreadPoolTaskExecutor executor;
	public void sendVerificationCode(String mailAddress, String verificationCode) {
        Runnable r = () -> {
            Context context = new Context();
            context.setVariable("code", verificationCode);
            String message = templateEngine.process("mail-verification-code.html", context);
            sendMail("환불 인증번호", message, mailAddress);
        };

        executor.execute(r);
    }

Async

  • 간--단
@Component
@Async("mailExecutor")
@RequiredArgsConstructor
public class SmtpMailConnector {
...
}

CorePoolSize, maxPoolSize, QueueCapacity에 대해

  • 밑의 블로그 참고했고 요약하자면!
  • Async를 통해 돌아가는 작업을 Task로 할게.
  • 일단 corePoolSize만큼의 태스트가 수행되고 QueueCapacity로 나머지가 들어가 QueueCapacity에 들어가지 못한 태스크는 이제 Pool에 등록된 Thread로 동작이 돼 그러면 현재 남아있는 Pool의 Thread는 maxPoolSize - corePoolSize야
  • 이 때 Task가 MaxPoolSize넘게 들어오게 되면 예외가 발생하게 돼 TaskRejectedException

참고

기준

  • MaxPoolSize를 크게 잡는 것도 비용이라 생각해서 작게 잡았어.
  • corePoolSize를 작게 잡은 이유는 지금 우리 서비스에서 저 정도로 메일서비스가 많이 그리고 오랜시간을 잡아먹는지 생각해봤어.
  • QueueCapacity는 혹시 몰라서 다른 애들보다 크게 잡아봤어!

@DWL5
Copy link
Collaborator Author

DWL5 commented Oct 26, 2021

내가 처음에 AsyncConfigurerSupport을 상속해서 한 방법은
아무 설정없이 @async를 쓸 때 사용하는 Default Exectutor를 설정한 것이었어!

그래서 Mail관련 Exectutor를 Bean으로 빼는 작업을 진행했어
Qualifier를 쓰면 각 작업마다 Runnable 구현 등 부가적인게 추가돼어서
@async에 Bean이름을 넣어 쓰는 방법을 택함!
클래스에 붙혀 주면 해당 클래스의 Public 메소드에 다 적용이 돼네! 로키 땡큐

그리고 Thread Pool 설정 의논해 보고 싶어!!
파즈 말 처럼 core size를 5로 잡는다 치면,
QueueCapacity 100정도로 크게 잡고
max size또한 5로 잡을까?
QueueCapacity가 100정도 되면 max size가 의미 있는 건지 싶어서...

@Be-poz
Copy link
Collaborator

Be-poz commented Oct 26, 2021

Interface나 class를 구현하는 방식은 default executor인 SimpleAsyncTaskExecutor 를 사용하지 않고 다른 executor를 사용하기 위해서 구현/상속을 한다는 것이다. 즉, 여러 executor를 사용하기 위해서는 위의 interface/class를 구현/상속 하는 형태를 띄면 안된다.
우리는 여러 executor를 사용할 수 있다는 가정을 했기 때문에 구현/상속 없이 @EnableAsync 만 붙여주었다.

이렇게 이해했는데 맞게 이해한거 맞나 ? 그리고 현재 @Qualifier를 사용안한 이유는 하나의 executor bean만 만들어 줬기 때문이지 ??
그런데, 다른 executor bean이 추가된다고 하더라도 @Qualifier를 사용할 이유는 없을 것 같다 !!
image
@Async( {요기} ) 에 빈을 입력하는 방식이고,
image

matching the qualifier value (or the bean name)

인걸 보아서는 @Qualifier value도 저기에 쓰는 것 같은데, 애초에 우리가 AsyncConfig.class 에서 빈 이름을 지정해주니깐 사용할 이유가 없는듯?

지금 pool size들을 지정하지 않은 것도 확실하게 결정된 것이 아니라서 그렇지 ?
나는 제안 하나 하자면 ExecutorService 를 사용할 때 보통 newFixedThreadPool 를 사용하는데 이 풀의 설정은 corePoolSize와 maxPoolSize를 똑같게 갖고 가는거야! 그리고 queue size는 디폴트로 두고.
한 마디로 말하자면, corePoolSize가 꽉 차게되면 대기 queue에 들어가게 되는데 이 queue는 꽉 찰 일도 없을 뿐더러, 꽉 찬다고 가정한다고해도 maxPoolSize가 corePoolSize와 같기 때문에 늘어나지도 않어. 우리가 처음에 만든 thread pool 딱 그 개수로만 관리를 하는거야.
내가 생각하기에는 스레드를 계속 만들고 하는 작업이 비용이고 최악의 상황에서 계속해서 만드는 작업이 일어날 수 있으니깐 이 방법을 많이 채택하는 것이 아닐까? 라고 생각해

다들 어떻게 생각하시는지? 그리고 내가 이해한 바가 맞는지 수리가 검토 해줘

Copy link
Collaborator

@Rok93 Rok93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수리가 남겨둔 코멘트 전부 확인했어 내가 이해를 잘 못해서 한참 이것저것 보다보니 이제 이해했다! 😃
빈으로 등록하려는 mailExecutor의 설정 값들이 중요할 것 같은데, 일단은 많은 리소스를 필요로할 것 같지 않다는 의견에 동의하기 때문에 파즈가 설정하자는대로 항상 크기가 고정되게 설정해보는 것도 좋을 것 같아.

추후 사용자가 많이 늘어나서 동시적으로 메일을 전송할 일이 많아진다면 maxPoolSize를 corePoolSize보다 크게 잡아서 유동적으로 pool size가 바뀌게 하는 것이 좋을 것 같아

(이 설정 값만 결정나면 될 것 같아서 approve 할게)

Copy link
Collaborator

@Joyykim Joyykim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번 PR 내용은 내가 공부를 따로 못했는데 다들 좋은 내용을 쉽게 설명해줘서 이해가 잘 됐어!
그리고 난 newFixedThreadPool 찬성. 신경쓸 점이 많이 없어서 좋아.

고생했어 수리!!😀

}).fail(function (e) {
alert("실패!")
});
await jQuery.ajax({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그건 따로 말하고 이건 그냥 삭제해도 될거같은데? 어차피 이거 쓰느 사람없고, 지금 api 서버에도 배포되어 있어서 아주 불편해

@DWL5
Copy link
Collaborator Author

DWL5 commented Oct 27, 2021

interface나 class를 구현하는 방식은 default executor인 SimpleAsyncTaskExecutor 를 사용하지 않고 다른 executor를 사용하기 위해서 구현/상속을 한다는 것이다. 즉, 여러 executor를 사용하기 위해서는 위의 interface/class를 구현/상속 하는 형태를 띄면 안된다.

-> 이거는 아닌 것 같아! @Async( {요기} ) 에서 {}에 아무것도 입력 하지 않는다면 SimpleAsyncTaskExecutor를 사용하거나 우리가 getAsyncExecutor 재정의해서 리턴하는 Executor를 쓰는 것이고 만약 여러개를 사용하고 싶다면 Executor 빈을 생성해서 빈의 이름을 요기에 넣어주는 것 이고! @Async( {요기} )

default는 상속, 구현을 통해 정하고, 새로 추가하고 싶으면 Executor를 Bean으로 등록해서 Async(Bean 이름) 이런식으로 사용하는 듯!

@DWL5
Copy link
Collaborator Author

DWL5 commented Oct 27, 2021

그리고 Index.html 이번에 비동기 테스트하면서 사용을 좀 했어가지구..
근대 나도 지우고 싶어!
저 소스는 다른 레포지토리에 아카이빙 해놓고 지울까?

@Be-poz
Copy link
Collaborator

Be-poz commented Oct 27, 2021

아하 그냥 SimpleAsyncTaskExecutor 를 바꿔줄 때에 그렇게 쓰는거구나 오케

@DWL5
Copy link
Collaborator Author

DWL5 commented Oct 27, 2021

newFixedThreadPool을 사용하는 것으로 고쳐봤어!

newFixedThreadPool new LinkedBlockingQueue를 사용하고,

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

block 이라는 의미는 Queue가 꽉찼을때의 삽입 시도/Queue가 비어있을 때 추출 시도를 막는다는 것이야.
이러한 block 기능 덕분에 thread-safe하대.

많은 thread들이 해당 queue에 접근해서 데이터를 넣고 뺄 때,
꽉차는 경우와 비어있는 경우에 발생할 수 있는 문제를 block 시켜줘서 thrad-safe한 듯

LinkedBlockingQueue는 따로 capacity 설정을 하지 않으면 capacity가 Integer.MAX_VALUE으로 설정돼

    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

아래 테스트를 통과해 corethread 수가 5개로 설정하고 100개의 일을 돌렸을 때,
queue사이즈는 95개

   @Test
    public void testThreadPoolTaskExecutor() throws InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5,
                new CustomizableThreadFactory("mail-executor"));

        // 100개의 작업을 수행
        IntStream.range(0, 100).forEach(i -> threadPoolExecutor.execute(getTask()));


        int poolSize = threadPoolExecutor.getCorePoolSize();
        int queueSize =  threadPoolExecutor.getQueue().size();
        assertThat(poolSize).isEqualTo(5);
        assertThat(queueSize).isEqualTo(95);

그리고

return (ThreadPoolExecutor) Executors.newFixedThreadPool(5,
                new CustomizableThreadFactory("mail-executor"));

에서 new CustomizableThreadFactory("mail-executor"))는 thread의 이름을 정해주기 위해서 설정했어!

  • 설정 안하면 thread 이름 pool-1-thread-1
2021-10-27 14:43:32.236 pool-1-thread-1 64446 [INFO ] [c.e.t.common.util.SmtpMailConnector     ] : sendChargeComplete
  • 설정 하면 thread 이름
2021-10-27 15:20:12.908 mail-executor1 65038 [INFO ] [c.e.t.c.c.GlobalExceptionHandler        ] : sendChargeComplete

참고

@Be-poz
Copy link
Collaborator

Be-poz commented Oct 27, 2021

확인완료~

@DWL5 DWL5 merged commit 0c73c7e into develop Oct 27, 2021
@DWL5 DWL5 deleted the feature/async-mail branch October 27, 2021 07:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 새로 추가될 기능 refactor 기능에 변경이 없는 코드 리팩터링 server 백엔드 관련 이슈
Projects
None yet
Development

Successfully merging this pull request may close these issues.

메일전송을 비동기로 처리한다.
4 participants