서버에서 주기적으로 특정 로직을 수행하거나, 특정 시간에 푸시 예약을 발송하는 기능 등을 구현할 때 Scheduler를 사용한다. 이러한 기능들은 API 호출 또는 사용자의 이벤트 시에 처리하는 것들이 아니므로 모두 비동기적으로 작업이 이루어지는데, 이들 간에 작업이 쌓이거나 앞단에서 이루어지는 API 호출 등의 작업과 동시적으로 이루어져 병렬 수행할 수 있는 크기의 최대치를 넘어가게 되면 서버가 급격히 느려지거나 장애가 발생하게 되는 것이다.
현재는 스프링부트 내에 있는 Spring Scheduler 라이브러리를 사용해 cron으로 예약을 일괄적으로 걸도록 구현된 상태이지만, 이러한 작업을 효율적으로 최적화하기 위해 메시지 큐를 사용할 수도 있다.
Spring Scheduler를 이용할 때는 cron 표현식을 각각의 부모자식 마다 지정해둔 뒤, Trigger로 예약한 작업을 수행하는 방식으로 구현을 했다.
이때 부모와 자식 모두 답변을 완료했다는 특정 조건을 충족했을 떄만 파이어베이스로 알림을 보내는 함수를 호출한다.
다른 스케줄링 작업 외에 푸시알림을 구현한 부분은 SQS 대기열에 메시지를 추가하기 위해 SqsPrducer에서 정의한 produce()
를 호출한다. 그렇다면! 스케줄링 작업 역시 이와 마찬가지로 예약된 시간+특정 조건에 달성하면 produce()
호출과 같이 SQS로 보내주면 되는 것이다.
어차피 SQS에 추가한 이후부터는 다른 푸시 메시지와 동일하게 리스너를 통해 consume하면 되는 것이므로 어떻게 SQS 대기열로 보내느냐가 관건인 것이다.
그렇다면 크게 두 가지로 나눠서 해야할 일을 정리해볼 수 있겠다.
⇒ 결론적으로 도입 가능한 방식
최후의 방법은.. 가장 별로라고 생각되지만 현재 API 서버에 있는 SqsProducer의 역할을 알림서버에도 함께 두는 방식이다. API 서버와 알림 서버의 역할을 명확히 분리해야 하는데, ①스케줄러를 API 서버에 옮기거나 ②SQS 대기열에 추가하는 SqsProducer를 알림 서버로 옮기거나 둘 중 하나를 포기하는 게 최선의 방법인 것 같다. 하지만 이렇게 하면 단순히 파이어베이스에 보내는 작업을 병렬처리 하는 것을 SQS 대기열에 추가하는 것으로 바꾸는 것밖에 안되기 때문에 실질적으로 동시다발적으로 발생하여 부하를 최소화 하고자 했던 본래 스케줄링 작업의 변화는 없는 것이다.
그럼에도 스케줄링 작업을 SQS와 연동하여 처리함으로써 보장되는 장점은 비동기 작업 처리 및 안정성을 확보하면서 시스템의 확장성을 향상시킬 수 있다고 한다..
스케줄러 자체가 항상 알림에만 적용되지 않을 수 있다! 즉, 파이어베이스로 푸시 메시지를 보내는 작업 외에는 모두 DB를 조회해서 특정 조건에 대한 검사를 반복적으로 수행하는 것이므로 엄연히 말하자면 API 서버에 두는 것이 더 적절하다고 판단했다
→ 비동기로 이루어지는 스케줄링 작업과 일반 API 작업이 같은 쓰레드 풀을 공유함으로써 풀이 꽉 차거나 누수가 발생하는 문제가 빈번히 발생했다.
결국, 멀티모듈과 서버 분리의 이점을 극대화하기 위해서는 스케줄링 작업을 알림서버에 두는 것이 최선이었고 쓰레드 풀을 API 서버와 알림서버 각각에 두고 사용하다보니 훨씬 서버의 부하가 적게 들었다.
현재는 스케줄링 작업을 활성시키기 위한 신호를 API 서버에서 SQS로 보내고, 이 신호를 알림 서버의 Listener가 대기열에서 가져오면 작업을 예약하는 방식으로 구현되어 있다.