개발 이야기

[Dev] 큐를 사용한 아키텍처에 대한 고민 (메세지 단위)

Razelo 2024. 5. 14. 10:36

최근 만들고 있는 어플리케이션에서 큐를 사용하게 되어 큐를 사용하는 아키텍쳐에 대한 고민을 해봤다. 

 

복잡하게 데드레터 정책까지 가져가면서 큐의 메세지에 대한 retry를 실행하려하진 않았다. 실패한 메세지의 경우 어차피 깨진 상태를 갖고 있다고 가정하고 warn 로그를 남기고 아카이브에 해당 작업을 실패로 기록한 뒤 큐에서 곧장 소비해서 없애버리는 정책을 가져가고자 했다. 

 

큐에 넣을 메세지는 어떤 작업을 지시하는 Job에 대한 정보로 사용하고자 했다. 그러다보니 api endpoint를 한 개만 남겨두고 기존에 만들어뒀던 api들을 모두 없애버리는 방법도 있었다. 한 개의 endpoint는 queue에 submit하는 역할만 하는 일종의 producer전용 api가 되는 것이다. 반대쪽 consumer만 꽤 유연하게 만들어주면 복잡함을 줄여주면서도 쓸만해질 것 같았다. 큐에서 뽑은 메세지를 보고 해당 메세지가 무슨 Job인지 결정해서 그 작업만 하면 된다. producer consumer는 단순화로써 꽤 좋아보였다. 

 

참고로 내가 하려던 작업은 여러 객체를 등록하는 작업이었다. A라는 등록 요청이 있으면 a1, a2, a3... 등 수십개의 객체를 등록해야하는 작업이다. 그리고 목표는 이 로직의 소요시간과 성능을 개선하는 것이었다. 

 

큐를 사용하는게 외부의 무언가를 가져다쓴다는 점에서 썩 내키진 않았지만 그렇다고 해도 ECS Task가 여러개 뜰텐데 인메모리에 상태를 저장해두고 쓰는 것도 말이 안된다고 생각했다. 큐를 쓰기는 싫었지만 단일한 상태를 보존해줄 싱글한 공간이 필요했다. 그래서 큐를 쓰게 되었다. 

 

큐를 도입하기 전에 이미 코루틴을 사용해서 a1, a2, a3... 를 동시에 처리해버리도록 개선했고 십수분이 걸리는 작업이 25 ~ 30초 정도로 줄여놓았다. 간헐적 Fail에 대한 원인도 찾아서 개선하여 꽤나 좋은 시도였다. 그런데 여기서 고민했던 것은 객체 등록 작업인 a1, a2, a3.... an 등록에서 이 an을 개별 메세지로 만들어서 큐에 쏴야할지에 대한 고민이었다. 현재는 A라는 등록 요청을 queue의 message단위로 사용하고 있다. 그리고 spring scheduler가 돌면서 큐에서 메세지를 뽑고 해당 등록 요청 작업을 실시하는 것이다. 그리고 하위 a1, a2, ... an개의 객체 등록은 코루틴으로 나눠서 동시 처리했다. 이 구조에서 작업 단위를 바꿀지에 대한 걸 고민하고 있는 것이다. 큐 메세지의 단위 말이다. 

 

큐를 통해 producer consumer 구조를 사용한다면 api를 하나만 유지하고 아키텍쳐가 깔끔해지는데 대신 큐의 특성으로 인해 각 객체 등록 작업을 동시처리할 수 없게 되는 것이다. 코루틴으로 스레드풀을 활용한 동시처리로 개선했던 이전 구조의 장점이 사라져버리는 것이다. 큐의 메세지는 동시처리하기 불가능하기 때문이다. 물론 message를 여러 건 한번에 읽을 수 있겠지만 근본적으론 an가 몇개인지 예측해서 뽑기도 불가능하므로 결국엔 순차처리로 봐도 무방하다. 그리고 그렇게 여러개를 뽑을 거라면 모든 등록 요청에 대한 정보인 A를 메세지 단위로 받아서 A를 받았을때 모든 하위 a1, .... an을 등록하게끔 하는거나 별반 차이가 없다. 

 

당장 편한걸 선택하자면 아키텍쳐를 바꾸지 않는 것이다. 그렇지만 몇번의 의견을 주고받으면서 한번 멈춰서 생각해볼 주제라고 생각되었다. 

 

지금 당장 드는 생각을 정리하자면 굳이 a1, a2 개별 단위를 queue message로 사용하지 않아도 될 것 같다고 생각한다. 어차피 동시처리하기 때문에 10개던 20개던간에 똑같은 처리 시간이 걸린다. (25 ~ 30초 내지) 다만 코루틴만 여러개 생겨서 리소스를 좀 먹을 뿐이다. message처리 시간도 어느정도 균일하게 예측가능하기도 하다. 그렇다면 그냥 지금 상태를 유지해도 되지 않을까라는 생각이 든다. 

 

내일이 쉬는 날이라 자기전에 생각이 나서 간단하게 기록해봤다. 

 

참고로 SQS를 사용하는데 설계 시 고민해야할 포인트를 몇가지 기록해두자면 다음과 같다. 

SQS는 일정 시간 후 다른 곳에서 해당 Message를 가져다 쓸 수 있게 하도록 하는 시간을 정해둘 수 있다. 그래서 각 message를 Job에 대한 지시 정보로 활용할 생각이라면 왠만하면 균일한 소요시간을 산정할 수 있는 작업 단위로 queue message를 쪼개는 것이 낫다. 작업 소요 시간이 균등하지 않으면 해당 시간을 몇으로 잡을지 애매해지게 되기 때문이다. 

또한 생성/소비/데드레터 정책은 초반에 러프하게라도 고민해보고 작업하는 것이 좋다. 안그러면 나중에 고민하게 되는데 그러면 이미 있는 코드베이스 때문에 고민 중 망설여지는 부분이 생기기 마련이다. 

 

반응형