Skip to content

Conversation

@le2sky
Copy link

@le2sky le2sky commented Jan 11, 2025

기본적인 구현 아이디어는 모듈러 연산인데요. 모듈러 연산의 단점을 극복하기 위한 합리적인 대안이 존재하는지 고민하기 위해서 구현했어요.
생각과 방법을 아래에 간략하게 요약해둘게요.

맥락

  • 모듈러에 필요한 값은 server count, server index 입니다.
  • 이 방식의 단점은 환경 변수를 관리하는 것입니다.
  • 메일 전송 시간대에 유동적으로 서버의 수가 변경되는 환경에서 환경 변수를 어떻게 관리하지? 가 고민의 시작점입니다.

구현 아이디어(리더선출-브로드캐스팅)

  1. 분산 잠금으로 리더 노드를 선출을 한다.
  2. 팔로워 노드가 /loyalty 채널에 메시지를 전달한다. → 127.0.0.1:8080, 127.0.0.1:8081
  3. 팔로워 노드의 메시지를 취합한 리더는 다시 메시지를 브로드캐스팅한다. (컨슈머 그룹 ID를 랜덤으로 생성해서 파티션 할당)
    1. 127:0:0:1:8080 number = 1, count = 3
    2. 127:0:0:1:9090 number = 2 count = 3
  4. 리더는 잠금을 해제한다.

구현에 대한 문제

  • 해당 방식에서 LeaderTask를 실행하기 이전에 팔로워 메시지를 수집하는 기간인 5초를 설정했는데요. 실제 수집 시간이 5초 보다 이하인 경우, 리소스 낭비이며, 이상인 경우 누락이 발생해요. 정확한 시간을 알아내기 어렵다는 문제가 있습니다.
  • 관측자 노드 프로세스를 새로 띄우느니, 리더 노드를 선출하는 방식으로 구현했어요. 전송 과정 중에서 전송 노드들이 다운 되는 것은 큰 문제가 되지 않는다고 생각해요. 발송 과정에서 노드들이 다운되는 경우에는 누락건에 대한 재전송을 수행하면 되기 때문입니다. 하지만, 리더가 다운되는 경우에는 재선출이 필요해요. 이 부분이 구현에는 빠져있으나 추가적인 구현이 복잡해질 것으로 예상됩니다.

다른 대안

  • 서버가 실행할때, 원자적 연산 보장해주는 카운터를 증가 시킨다. 다운될떄, 카운터를 감소 시킨다. (체크인-체크아웃 방식)
    • (문제) 다운될때, 카운터를 감소 못 시킬 수도 있다.
  • 관측자 노드가 주기적으로 헬스체크를 받고 전송 시간대에 참여자 노드에게 친절히 알려준다. 혹은 DB에 server count, server index를 저장한다. (관측자 방식)

아무래도 이게 핵심

어떤 방식을 사용해도, 잘못된 server count, server index가 설정되는 일이 발생할 수 있습니다. 하지만, 적어도 server index가 중복될 일이 없다면, 누락건에 대한 재전송은 가능하다고 생각합니다. 따라서, 제가 생각하기에는 server index에 노드 간 중복이 없으면서, 전체 구독자 구간에서 누락이 발생할 케이스가 가장 작은 것이 좋은 대안의 기준이라고 생각합니다. (비슷하다면, 성능을 확인), 아 근데 subscribe_id가 UUID면 어쩌지..?

@3Juhwan
Copy link
Member

3Juhwan commented Jan 15, 2025

소중한 PR 감사합니다 😀
주키퍼, 카프카, 리더 노드 선출 등 엄청난 PR이네요!! 리더 노드 선출 알고리즘만 열심히 보다가 오늘 PR 처음 열었는데, 생각보다 모르는 키워드가 많아서 시간이 조금 더 걸릴 것 같아요. 오늘까지 리뷰하다 말씀드렸는데, 최대 이틀 정도 시간을 부탁드려요!
추가로 위 키워드를 보고 약간은 놀랐는데, 저런 기술을 시도할 수 있는 게 앵두랩이 아닐까 싶네요!

@le2sky
Copy link
Author

le2sky commented Jan 16, 2025

@3Juhwan

코멘트 늦어져도 괜찮습니다. 앵두랩이 아니면 이런 시도하기는 어려워요. 얼른 활성화되었으면 좋겠습니다. ㅎㅎ
PR에 포함되어 있는 내용이 상당히 많아서, 대화의 초점을 맞추면 좋겠다는 생각에 코멘트 추가로 남깁니다.
카프카와 비동기 메시징 방식에 대한 논의는 추후에 더 좋은 미션들에서 할 수 있다고 생각해요. (예를 들어, 분산 트랜잭션이라던가.. 메시징 기본기 미션이라던가)

제가 생각하는 핵심적인 4가지 논의 대상은 리더 선출, 팔로워 메시지 수집 기간, 브로드 캐스팅 방법, 리더 다운 상황 대책입니다.
현재는 리더 선출 = 분산락, 팔로워 메시지 수집 기간 = 5초, 브로드 캐스팅 = 카프카, 리더 다운 = 대책 없음으로 구현된 방식인데요. 논의 대상들에 대한 더욱 좋은 방법들이 존재하는지 탐구하고 싶어요. 그리고, 해당 방식에서 server index가 잘못 설정될 수 있는 케이스들을 식별해서 전반적으로 리더 선출-브로드캐스팅 방식이 좋은 방안인지 검증하고 싶어요. 참고로 카프카로 브로드 캐스팅하는 것은 다음 블로그 글을 참고했어요. -> Kafka: Broadcasting Messages Without Consumer Groups. 그리고, 데이터 중심 애플리케이션 설계 9장에 필요한 재밌는 지식들이 있어 보이네용

추가적으로 이 논의를 이끌어나갈 망쵸의 아이디어가 있으면, 말씀 남겨주세요.

++ 여담으로, 메일 전송 문제가 해결되도 SMTP 측 처리량 문제를 해결해야해요. 실제 운영중인 SMTP는 초당 14건을 처리하기 때문에, 전 세계 80억 인구한테 메일 1건씩 전송하려면 18년이 걸려요. AWS 측에 초당 처리량 증가 요청을 계속해서 늘리다보면 한계에 도달할 수 있다고 생각해요. 한계에 도달한 경우, 직접 SMTP 서버를 구축해야하거나, 여러 클라우드 서비스를 동시에 사용하는 방식을 사용할 수 있습니다. 그러면, 이떄 각 SMTP 서버의 처리량을 고려한 로드 밸런싱을 직접 구현해야할 수도 있는데요. 미션으로 나오면 재밌을 것 같아서 말씀 드립니다.

@le2sky le2sky changed the title [분산 메일 전송] 이하늘 미션 제출합니다. [분산 메일 전송] 이하늘 미션 제출합니다. (리더 선출-브로드캐스팅 방식) Jan 16, 2025
@3Juhwan
Copy link
Member

3Juhwan commented Jan 21, 2025

오래 기다리셨습니다~ 코드와 디스크립션을 읽고 최대한 이해하려 노력했어요! 그리고 아마도 거의 이해한 것 같아요. 우선, 이 코멘트에서는 위에 작성해주신 디스크립션과 코멘트를 읽고 든 생각을 공유할게요 😀 너무 늦어지는 것 같아서 지금까지 생각한 내용을 공유하는 것이고, 이후에 좀더 사고하다가 생각나는 아이디어가 있다면 추가 코멘트로 남길게요!

개요

이 미션의 목표는 "메일 발송을 중복 없이 모든 사용자에게 안정적으로 전송한다"에요. 그리고 여러 서버 노드로 발송할 메일을 분배하는 해결책을 다루고 있어요. 메일을 분배하는 데에는 모듈러 연산을 사용하고 있고요! 모듈러 연산 방식에서 중요한 총 노드 수노드 번호를 어떻게 관리하는 지에 대해 제안해 주신 방식이 3가지인 것 같아요.

  1. 리더 선출-브로드캐스팅 방식
  2. 체크인-체크아웃 방식
  3. 관측자 방식

3가지 방식에 대한 생각

3가지 방식 중에서는 리더 선출-브로드캐스팅 방식이 좋다고 생각해요. 리더 선출-브로드캐스팅와 대비하여 나머지 두 방식을 이야기 해볼게요!

관측자 방식은 서버 노드 이외에 별개의 노드를 두어 관측자로 지정하고, 관측자 노드가 리더를 선출한다고 이해했는데 맞을까요? 그렇다면 리더 선출-브로드캐스팅 방식이 유사하게 느껴지는데요. 메일 발송하는 서버와 관측자 서버를 구분한다는 점 말고는 차이를 모르겠어요. 괜히 노드를 하나 더 추가해서 관리 포인트만 늘리는 게 아닐까 싶어요. 다만, 관측자 서버를 둔다면 리더 선출-브로드캐스팅 방식보다는 단순할 것 같아요. 이것도 꽤 장점이 된다고 생각해요! 하지만 리더 노드를 선출하는 관측자 서버가 다운되는 상황은 어떻게 대처할지 고민이 필요하겠어요. SPOF 지점인 것 같고 이를 해결하려면 클러스터를 구성해야 할까요? 클러스터를 구성한다면 정말 리더 선출-브로드캐스팅 방식을 선택하지 않을 이유가 없을 것 같아요!

체크인-체크아웃 방식은 중앙 데이터베이스(MySQL, Redis 등)을 사용해서 각 노드가 카운트 업, 다운하는 방식이라고 생각했어요. 이 방식은 조금 더 불안정한 방식이란 생각이 들어요! 다른 두 방식은 특정 1개의 노드에 문제가 생길 경우에 전체 시스템이 영향이 가는 반면에, 이 방식은 어떤 1개의 노드라도 문제가 생기면 전체 시스템에 영향이 간다고 생각해요. 리더 선출-브로드캐스팅 방식은 리더 노드에 문제가 생기면 전체 시스템에 영향을 미치고, 관측자 방식은 관측자 노드에 문제가 생기면 전체 시스템에 영향을 미쳐요. 체크인-체크아웃 방식은 한 노드라도 정상적으로 중단되지 않으면 카운트 다운을 하지 못하고 정합성이 깨지게 돼요. 또는 네트워크 단에서 카운트 업 또는 다운이 유실되면 복구하기 어려울 것 같아요. 이것이 큰 단점이라 생각해요.

리더 선출-브로드캐스팅 구현 방식의 문제에 생각

해당 방식에서 LeaderTask를 실행하기 이전에 팔로워 메시지를 수집하는 기간인 5초를 설정했는데요. 실제 수집 시간이 5초 보다 이하인 경우, 리소스 낭비이며, 이상인 경우 누락이 발생해요. 정확한 시간을 알아내기 어렵다는 문제가 있습니다.

다음은 읽고 생각난 아이디어인데 의견이 궁금해요!

[1] 메일 발송을 안정적이고 신속하게 하기 위해 필요한 노드 수를 X라고 가정하자.
[2] 팔로워 메시지 수집 기간을 1초(초기)로 설정한다.
[3] 응답 노드가 X개 될 때까지 기다린다.
[4] 시간 안에 X개의 노드에게 응답을 받지 못한다면, 수집 기간을 2초로 늘린다.
[5] 응답 노드가 X개가 될 때까지 [3]-[4]를 반복한다.

관측자 노드 프로세스를 새로 띄우느니, 리더 노드를 선출하는 방식으로 구현했어요. 전송 과정 중에서 전송 노드들이 다운 되는 것은 큰 문제가 되지 않는다고 생각해요. 발송 과정에서 노드들이 다운되는 경우에는 누락건에 대한 재전송을 수행하면 되기 때문입니다. 하지만, 리더가 다운되는 경우에는 재선출이 필요해요. 이 부분이 구현에는 빠져있으나 추가적인 구현이 복잡해질 것으로 예상됩니다.

구현한 방식에서 리더 선출 행위의 트리거는 스케줄러에요. 메일 발송하는 시점에 스케줄러가 작동하여 리더를 선출하고 메일을 발송해요. 하지만 리더 노드가 다운되는 상황에서는 재선출이 필요하기 때문에 새로운 트리거가 필요하겠어요. 재선출 트리거를 발생하는 게 좀 어려워 보이는데요. 어떻게 리더 노드가 다운되었다는 것을 각 노드가 어떻게 감지할 수 있을까요? 브로드캐스팅이 제대로 되지 않았다면 이걸로 어떻게 감지할 수 있을 것 같지만, 아직 카프카 쪽 구현을 확인하지 못해서 정확히는 모르겠어요. 확실히 리더 노드가 다운되면 구현이 복잡해 지겠어요.

자세히 써보려 노력했는데, 그러지 못했다면 피드백 부탁드려요 🙇 읽으면서 이해하기 어려운 부분도 말씀해 주세요! 최대한 빨리 확인하고 답변할게요.

ps. 개인 사정으로 코멘트가 늦은 점 미안합니다. 계속 아프기도 하고, 그로 인해 밀린 일을 처리하느라 늦게 되었어요... 참 쉽지 않은 1월이네요 :<

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants