Skip to content

Issue #2

gaya edited this page Nov 5, 2023 · 2 revisions

비동기로 메일 알림 보내기

개요

메일 알림 기능을 만들고 싶었습니다.
사용자들이 관심이 가는 공연에 즐겨 찾기를 해놓으면, 해당 공연의 일정이 등록되었을 때
상품을 즐겨찾기를 등록한 사용자들에게 메일로 알림을 보내주는 기능이죠.

메일 알림기능을 만들면 그 과정은 다음과 같을 것입니다.

1. 즐겨찾기를 등록한 사용자를 DB에서 조회한다.
2. 발송할 이메일 주소와 메일 내용을 메일 서버로 전송한다.
3. 메일 서버에서 응답이 올때까지 기다린다.
4. 다음 발송할 이메일 주소와 내용을 메일 서버로 전송한다.

사용자가 별로 없을 때는 상관이 없겠지만, 사용자가 늘어나면 성능적인 부분에서 문제를 일으킬 가능성이 보입니다.
메일 서버로의 요청과 응답 중에 스레드에 I/O Blocking이 걸리기 때문입니다.

해당 방식의 문제점은 메일 서버가 메일을 보내고, 다시 응답을 보내줄 때까지 기다리는데 시간이 소요 된다는 점입니다.

비동기 방식 사용

저희는 비동기 통신의 구현 방법 중 kafka를 사용해보았습니다.

스레드 입장에서는 그저 kafka에게 메일을 발송하라는 메세지를 넘겨주면 됩니다.
kafka는 메일 발송 및 메일 서버와의 응답을 기다리는 역할에게 메일을 발송하라는 메세지를 전달해줄 것입니다.
그러면 스레드가 blocking 되지 않으면서 메일을 전송하는 동안에도 다른 작업을 처리할 수 있을 것입니다.

변경 전

저희 At_ticket 프로젝트는 하나의 프로젝트에 모듈이 여러개로 나누어져 있는 멀티모듈로 구성되어 있습니다.
각각의 모듈이 각각의 기능을 처리하죠.
이번에 공연 등록 알림을 담당할 모듈은 상품과 공연 기능을 담당하는 Product 모듈입니다.

과정은 다음 그림과 같습니다.

1. Product모듈에서 대상 회원을 조회해서
2. 메일 서버에 메일 발송 요청을 보냅니다. 

메일 서버에서 메일을 발송하고 Product 모듈에 다시 응답을 돌려줄 때까지,
Product 모듈의 스레드는 blocking될 것입니다.


변경 후

추후 확장성을 고려하여, kafka-app 모듈 이라는 kafka를 통하여 처리할 기능을 처리해줄 모듈을 추가하였습니다. kafka-app 모듈에서 메일 서버로의 요청과 대기를 담당해줍니다.

kafka를 적용하여 비동기 식으로 변경 한 메일 알림 기능은 다음과 같습니다.

1. Product모듈에서 대상 회원을 조회합니다.  
2. Product모듈의 kafka의 producer를 통하여 kafka의 broker에 메세지(메일 주소, 메일 메세지)를 전달합니다.
3. kafka-app 모듈은 kafka consumer를 통해 해당 메세지를 전달받습니다.
4. kafka-app 모듈은 메일 서버에 메일 발송을 요청합니다.



메일 서버에 요청을 보내고 요청을 기다리는 역할은 kafka-app 모듈이 맡아서 처리하게 됨으로써 Product 모듈은 더 이상 메일 서버의 응답을 기다리지 않아도 됩니다.

테스트

그러면, 직접 메일 알림을 보내는 방식과, 카프카를 이용하여 비동기 방식으로 메일 알림을 보내는 각각의 방식을 테스트 해보겠습니다.

테스트 방식

* DB에 특정 상품을 즐겨찾기 한 유저를 가정하여 발송할 이메일 주소 정보를 많이(200개) 준비한다.  
* Postman으로 메일 알림 발송 api를 호출한다.

1. Product 모듈에서 직접 메일 서버를 호출하여 이메일을 200통 전송한다.
2. 카프카를 이용하여 메일 서버를 호출하여 이메일을 200통 전송한다.

1. Product 모듈에서 직접 메일을 전송하기


Product 모듈에서 직접 메일 알림을 전송하도록 메일 전송 로직을 수정하고,
postman으로 메일 알림 API를 호출하여 보았습니다.

img ▲ Product 모듈에서 직접 메일을 전송하는 중

Product 모듈에서 직접 메일을 보내고 있기 때문에 메일 전송 api를 호출하였을 때 response가 바로 돌아오지 못합니다.
I/O 처리중이기 때문에 스레드에 blocking이 걸리기 때문입니다.

물론 스레드가 한개만 있는 것은 아니기 때문에, 메일 전송을 하고 있는 중에도 다른 api를 호출하는데는 문제가 없을 겁니다.
하지만 사용할 수 있는 스레드가 줄어들기 때문에 전체적인 성능에 영향을 미칠 수 있을 것이라고 추측할 수 있습니다.
또한 api를 호출하였을 때 답이 돌아오는 시간이 오래걸린다면 사용자 경험에도 좋지 않은 영향을 미칠 것 입니다.

img
▲ 직접 메일을 전송하였을 때 응답에 걸린 시간

Product 모듈이 메일을 전부 전송하고 나서야 api 호출에 관한 response를 받을 수 있었습니다.
200통을 전송하는데 1분 22초의 시간이 걸렸습니다.
보내야 할 사람이 늘어나면 늘어날 수록 응답시간은 점점 길어질 것입니다.

2. 카프카를 이용하여 메일 전송하기


이번엔 카프카를 이용하여 메일을 전송해보겠습니다.

img
▲ 카프카를 이용하여 메일 서버에 메일 전송 요청

img
▲ 카프카를 이용하여 메일을 전송 응답에 걸린 시간

직접 메일을 전송하였을 때와 다르게, 카프카를 이용하였을 때는 응답이 1초 안으로 금방 돌아왔습니다.
스레드는 카프카에게 메일 전송 이벤트를 전달해주기만 하면 되기 때문입니다.

카프카가 메일을 전부 전송하는 데 걸린 시간은 직접 전송하는 것과 비슷한 1분 20초 정도가 걸렸습니다. 설정에 따라서 전송이 완료되는 시간을 달라질 수 있을 것입니다.

메일을 전송 주기도 카프카에서 설정할 수 있기 때문입니다. 프로그램의 상황, 메일 서버의 상황에 맞추어 전송 속도를 조절할 것도 카프카를 쓰는 장점이라고 할 수 있겠습니다.