동기 / 비동기 / Event

jungseo·2023년 6월 27일
0

Spring

목록 보기
12/23
post-thumbnail

동기

  • 코드 작성 순에 따라 작업 실행
  • 이전 작업 완료 후 다음 작업 시작
  • 작업이 빠르게 수행되는 경우, 순서와 작업간 의존성이 중요한 경우 등

비동기

  • 전의 작업이 완료되기를 기다리지 않고 바로 시작
  • 스레드 혹은 이벤트 핸들러에서 비동기적으로 실행 가능
  • 백그라운드에서 동시에 여러작업 진행 가능
  • 네트워크 호출, 긴 시간이 걸리는 작업 등

1. ExcutorService 인터페이스

  • 여러 작업을 비동기적으로 처리하기 위해 제공되는 JAVA 라이브러리

(1) 생성

  • newFixedThreadPool(int) : 인자 개수만큼 고정된 스레드풀을 생성
  • newCachedThreadPool(): 필요할 때, 필요한 만큼 스레드풀을 생성
  • newSingleThreadExecutor(): 스레드가 1개인 ExecutorService를 리턴. 싱글 스레드
ExecutorService executorService = Executors.newFixedThreadPool(4);
ExecutorService executorService = Executors.newSingleThreadExecutor();

(2) 작업 할당

  • submit()
    • 작업을 할당하고 Future 타입을 반환
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(() -> {});
  • execute()
    • 반환 타입이 void로 작업의 결과나 상태를 알 수 없음
  • invokeAny()
    • 작업을 Collection에 넣어 인자로 전달하면 실행에 성공한 작업 중 하나의 반환값을 반환
  • invokeAll()
    • 작업을 Collection에 넣어 인자로 전달하면 모든 반환값을 List< Future <>>로 반환

2. @Async

  • 해당 애너테이션이 붙은 메서드가 비동기적으로 실행
  • 클래스 레벨에 @EnableAsync를 붙여 사용
  • 간단한 방식으로 비동기 방식 사용 가능

Event

  • 서비스 간의 의존성을 줄이고 확장성과 유지보수를 위해 사용
  • 예를들어 회원 가입 자체를 하나의 이벤트로 설정해 회원 가입시 알림 메일 발송 등 부가적인 프로세스들을 Service 클래스에서 분리하여 처리
  • 아래 예시는 트랜잭션 내부에 비동기적으로 실행되는 회원가입시 확인 메일 발송 기능 중 메일 발송에 실패하면 가입된 회원의 정보가 삭제되는 기능 구현

1. Event 클래스

  • 이벤트를 처리하는데 필요한 데이터를 가짐
@Getter
public class MemberEvent {
    private Member member;

    public MemberEvent(Member member) {
        this.member = member;
    }
}

2. EventPublisher

  • 이벤트를 발행하는 역할
  • ApplicationEventPublisher 인터페이스를 사용
  • 스프링 컨텍스트가 해당 인터페이스를 구현한 빈을 내부적으로 등록
@Slf4j
@Transactional
@Service
public class MemberService {
    private final MemberRepository memberRepository;
    private final ApplicationEventPublisher eventPublisher;

    public MemberService(MemberRepository memberRepository, ApplicationEventPublisher eventPublisher) {
        this.memberRepository = memberRepository;
        this.eventPublisher = eventPublisher;
    }

    public Member createMember(Member member) {
        verifyExistsEmail(member.getEmail());
        Member savedMember = memberRepository.save(member);
        log.info("# Saved member");

//        (1)
        eventPublisher.publishEvent(new MemberEvent(member));

        return savedMember;
    }
    		...
}
  • publishEvent() 인자에 Object 타입을 넣어 이벤트 발행

3. @EventListener

  • 이벤트 핸들러 메서드에 붙이며 이벤트가 발생하면 해당 이벤트와 매칭되는 이벤트 핸들러 메서드를 실행
  • 발행된 이벤트의 타입에 따라 매칭
@Slf4j
@EnableAsync
@Component
public class MemberEventListener {
    private MemberService memberService;
    private EmailSender emailSender;

    public MemberEventListener(MemberService memberService, EmailSender emailSender) {
        this.memberService = memberService;
        this.emailSender = emailSender;
    }

    @EventListener
    @Async
    public void sendEmail(Member member) throws Exception {

//        (1) ExecutorService 사용 시
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
//        executorService.submit(() -> {
//            try {
//                emailSender.sendEmail("any email message");
//            } catch (Exception e) {
//                log.error("MailSendException happened: ", e);
//                memberService.deleteMember(member.getMemberId());
//                throw new RuntimeException(e);
//            }
//        });

        try {
            emailSender.sendEmail("any email message");
        } catch (Exception e) {
            log.error("MailSendException happened: ", e);
            memberService.deleteMember(member.getMemberId());
        }
    }
}

0개의 댓글