Social Architecture
2025년 회고용 (7월 ~ 12월)
소셜 서버의 경우, Feed, Live, Club, UD(Up, Download)의 분야로 나눠져있고, 각 서버는 각기 다른 도메인을 가졌다.
FEED
Feed는 주로, 틱톡의 피드 추천 기능이나, 피드 업로드 그리고 댓글, 인기댓글 등의 기능을 제공했고, 댓글을 올리거나 반응이 올라오면 자신의 팔로워들에게 알림이 or Push 메시지를 보내는 식의 기능을 지닌다.
인기 댓글과 댓글 좋아요 기능 개발
작업기간 (9월~10월)
댓글 좋아요 기능
Feed의 댓글 기능은 2020년 이후에 추가로 개발되지 않았던 기능이라 초기 피드 개발했던 상황 그대로였다. 일단 게시글당 Post 테이블에 이력이 저장되고, Post 테이블과 1대 다 관계인 Comment 테이블이 있었던 상황이었다. 그리고, 고정 핀의 기능이 있어서 PinnedComment 테이블도 있었는데 이 테이블의 역할은 고정핀인지 아닌지를 구분해주는 역할이다. 일단 내가 작업한 것은 Comment 테이블에 좋아요를 받은 수의 컬럼을 추가하고, PostCommentLike 테이블을 추가해서, 좋아요를 보낸 유저의 리스트를 저장했다.
좋아요를 받으면 Kafka로 좋아요를 누른 토픽을 전달하고 토픽의 내용은 좋아요를 누른 게시글 id, 댓긇 id, 좋아요 누른 사람의 id를 보내준다. 여기서 kafka lag이 밀리진 않았던 것 같다. (간간히 중국서버의 경우 cpu가 낮은 서버로 처리하게되면 lag이 쌓여서 알림이 오는 적이 있었다.)
특히, Push나 알림센터의 설정을 Core 서버에서 받아서 푸시를 보내야되는지, 알림만 보내는 것인지 체크를해서 보냈었다. 그리고 댓글을 좋아요 누른 사람이 작성자라면 알림은 보내지 않도록 처리했다.
인기 댓글
Feed는 게시글을 1시간동안 Redis에 캐싱해둔다. 게시글이 많고, 유저의 사용성에도 큰 영향을 미치는 영역이기 때문이다. 인기 댓글은 캐시가 다시 로딩될때 좋아요 수를 가장 많이 받은 댓글 한개에 대해서 뿌려지게 만들었다.
kafka 토픽의 등록의 경우, 알리 클라우드는 미리 등록을 해야됬고, 회사 클라우드는 그러지 않아도 되서 사전작업을 하는데 있어서 조금 주의가 필요했다.
이슈 정리
댓글 좋아요의 경우, 700ms 정도의 락을 잡고 테이블 두개를 반영시키도록 했다. 그런데, 댓글 갯수를 가져오는 부분과 댓글 이력간의 약간의 차이가 일어나서, 총 2건의 댓글 좋아요를 누른 유저들을 찾는 과정에서 기존에 subList(0,2)를 했었는데, 테이블 정합성이 순간 어긋나서 Index Error가 나고있었다. 그래서 limit(2)로 해당 로직 수정되었다. (어떻게 보면 락이 생각보다 짧아서 그랬을수도 있겠다는 생각이 들긴한다..)
인기댓글의 경우, 인기댓글이 수정되면 evict가 되어야되는데 해당 부분을 깜빡해서 나중에 BTS가 등록되었다. (이부분은 캐시를 쓰면 초기에 한번씩 점검을 해야될 것 같다.)
LIVE
Live는 유투브 라이브처럼 실제 라이브 방송에 대해서 채팅, 후원, 후원 리더보드, 팬 레벨 시스템 등을 제공하고 라이브 관련 이벤트가 많아서 이에 대한 이벤트 리더보드도 제공한다. 라이브 서버는 특유의 생명주기를 가진다.
LIVE 생명주기
라이브 생명주기는 스트리머의 생명주기와 시청자의 생명주기가 다르다.
라이브 아키텍처의 경우, 텐센트의 CSS 서비스를 이용했는데, CSS에서는 방송 화질에 대한 트래킹의 기능도 제공해줬고, m3u8이나 webRTC 포맷으로 시청을 할수 있도록 기능도 제공해줬다.
특히, 스트리밍 데이터의 경우, m3u8 파일을 이용해서 일종의 세그먼트 단위로 데이터를 받게된다. 이를 통해 타임스탬프를 알수있고 현재 방송 시점에 영상을 보여줄수 있다.
CSS의 경우, 라이브가 정상적으로 시작하면 callback 형식으로 방송시작을 알려주기도하고, 네트워크 불안정으로 인한 방송 종료 상황의 경우도 callback 형식으로 알려준다.
스트리머의 생명주기
게스트나, 합방의 경우 방송시작 후에 스트리머가 설정한다. 방송 종료의 경우, 특정 시간동안
방송 생성 -> 방송 시작 -> 방송 정지 -> 방송 종료
방송 재개
시청자의 생명주기
방송 참여 -> 방송 나가기 (스트리머의 방송 종료로 인한 방송 나가기도 포함)
LIVE 아키텍처
모니터링은 Promethus와 ES기반의 로그 서비스, Grafana를 사용했다. (가끔 pinPoint도 쓰긴했다.)
WSM
|
|---------- ZooKeeper
|
Client -- WS ----------API - - - KAFKA - - - LIVE-ASYNC
|
|
|
LIVE-BATCH
WSM은 2대 정도 있고, 이는 5대의 WS서버에 세션들을 관리해주는 역할을 한다. 에를들어, 합방으로 인하여 다른 WS서버에 있는 유저의 세션을 옮겨야되는 경우, 이를 옮겨주거나 특정 세션이 어떤 WS 서버에 있는지를 알려주는 역할을 한다. 이를 Presence 서버라고 한다.
WS는 일종의 게이트웨이 역할을 수행했다. 20대의 API로 가기전에 핋요한 기능들을 제한하고, 댓글이나 스트리머측 장비에서 오는 Data Node 정보들은 API 서버를 타지 않고 bypass로 전달하도록 되어있다.
LIVE BATCH의 경우, 후원 효과를 클라이언트에 BroadCast하거나, 그외에 라이브 생명주기에 필요한 작업들을 수행한다. 예를들어 방송이 종료되면 해당 방송에서 사용했었던 캐시를 정리하거나, 핀,뱃지 등록 작업 등을 수행한다.
그리고 주키퍼 서버에서 dataChange가 되면 API 서버마다 트리거시키거나
720P 트랜스코딩 제거 작업
작업기간 (7월~8월)
트랜스코딩 비용이 많이 나오는 상황이었는데, 방송 송출자가 많고, 시청자가 적은 상황에선 트랜스코딩 비용이 많이든다. 트랜스코딩은 하나라도 시청을 한 사람이 있는 경우에 돈이 나가게되는 구조라, 예를 들어 슈카월드처럼 많은 수의 사람이 들어오는 방송의 경우, 트랜스코딩 비용보단 네트워크 대역폭 이슈로 인한 비용이 이슈가 될 수도 있다.
트랜스코딩은 종류가 두개가 있는데, 클라이언트 사이드에서 하는 트랜스코딩 작업이있고, 서버사이드에서 하는 트랜스코딩 작업이있다. 클라이언트 사이드에서 하는 작업의 경우, 카메라 앱에서 영상을 찍고 특정 화질로 인코딩하는 점을 들수 있다. 반대로, 서버사이드의 경우 서버측에서 해당 화질에 맞게 영상을 인코딩해준다. 또한, 서버측에선 ABR 트랜스코딩 기능을 제공하는데, 이는 네트워크 상황에 따라 화질을 자동으로 바꿔주는 작업을 제공한다. 만약 클라이언트 사이드에서 트랜스코딩을 작업을 한 상황에서 서버사이드의 트랜스코딩 작업을 제거하고 원본 영상을 내려받게된다면 이는 서버비용을 줄일순있지만 그만큼의 버퍼 역할을 하는 것이 없어서 영상이 뚝뚝 끊길수도 있다. 그리고 영상의 세그먼트도 timestamp가 매순간 초기화된다고 들었던것같다..
트랜스코딩을 제거하려면 m3u8 파일에 특정 화질의 영상을 내려주지 않으면 된다. 따라서, 방송을 생성하는 시점에 해당 방송의 정보를 커스터마이징한 m3u8 파일을 S3같은 object Storage에 저장하고, 이를 시청자가 조회할때 내려주는 방식으로 처리했다. 그리고 해당 m3u8 파일의 경우 삭제 주기를 1달로 걸어서 자연스럽게 삭제되도록 작업했다.
해당 부분 개선작업을 하다가 결국 바우처를 더 받아서 매월 N천만원씩 비용을 절감했다.
라이브 스트리머 운영 개선 (핀,뱃지 캘린더 개발)
작업기간 (7월~8월)
Admin 내 핀뱃지 캘린더 FE,BE개발
캘린더 형태로 핀, 뱃지 기능을 예약하는 화면 및 기능 개발하는 작업. 스트리머의 경우, 지역마다 시간차이가 있었고 서버는 한국에 있어서, 한국시간을 해당 지역시간대로 FE에서 변경해주는 작업이 많았다. 예를 들어, 한국이 UTC+9이면 미국의 경우, UTC-4으로 표시되었다. 단 이것을 역으로 생각해보면 한국에서 미국 스트리머를 예약을 한다면, 미국 자정에 핀이나 뱃지를 달아주려면 미국 시간을 한국의 시간으로 바꿔서 작업을 해줬다. 이 작업은 모두 FE에서 모두 작업을 했었고, 각 리전을 운영하는 운영자들의 호평을 받았다.
핀 뱃지의 경우, 다건을 한번에 등록하는 기능이 필요했다. 그것도 필드를 하나파서 다건을 등록할수 있도록 했었다. 사실 이건 다건을 등록할수 있는 별도의 테이블을 하나 더 파는게 유지보수가 더 편했을 것 같다. 특히, 기존의 레거시 로직이 webflux를 사용하는데 Flux -> Mono -> Flux -> Mono 이런식으로 하나의 체인이 돌아가는 형태라 가독성이 너무 안좋았다…
이슈 정리
리스트간 비교를 contains를 했다가, 잘못된 비교를 하게되서 1분마다 pin이 바뀌는 이슈가 있었다. 예를들어, 1분엔 A가 나왔다가 2분엔 B가 나오고 3분엔 A가 나오는식으로.. 그래서 해당 부분은 HashSet equals을 통한 비교로 바꿔서 해결했다.
라이브 시작전에 기존에 핀이 등록되어있으면 제거하는 로직을 방어로직으로 넣었는데, 1분 배치가 돌기전에 방송을 껐다켜서 핀이 시간이 지났는데도 계속 가동하는 이슈가 있었다. 이 부분은 위 로직을 제거하면서 해결은 됬었는데. 추가로 이슈가 더있어서, 1분 마다 배치돌면서 아닌건들이 있으면 제거해주는 로직을 추가했다.
팬레벨 시스템
작업기간 11월 ~ 12월
시청자 팬레벨을 추가하는 대형 과제가 하나 생겼고, 채팅, 후원 금액, 투표를 이용해서 시청자간의 레벨을 실시간으로 계산하고, 각종 리더보드와 채팅등의 시청자 뱃지로 표시하는 작업과 월간 배치 작업이 수행되어 특정 레벨에 기준에 미달한 계정의 경우, 레벨 다운하는 과제였다.
채팅, 부스터, 후원금액은 redis hash로 넣고 레벨업을 했을 경우만 DB에 2차적으로 레벨업을 하고 zookeeper로 각 시쳥자의 웹소캣 서버로 broadcast하는 과정을 통하게 만들었다. 이는 Redis를 1차 캐시로 사용함으로 인하여, DB부하를 크게 줄일수 있었고 대상 모수를 최대한 낮춰가면서 월배치에 해당하는 사용자 수를 크게 줄일수 있었다.
그리고, 월배치의 경우, 각 리전별로 0시 0분에 트리거링되면서, 같은 시간대의 경우, 병렬 스레드로 실행될 수 있도록 작업하여 정합성 이슈를 줄일수 있았다. 이를통해, 안드로이드, ios, PC 등 다양한 OS의 개발환경에 맞춰서 팬레벨 시스템 정보를 제공하려고 노력했었고, 이전의 슈퍼팬 시스템과의 분리를 위해 로직적으로 버전 분기를 추가하는 작업을 진행하여, 100만명 이상 사용하는 플랫폼에서 이슈 발생률을 크게 줄일 수 있었고, 신규 사업 과제가 안정화 될수 있도록 큰 업적을 남겼다.
사실 어려운 것은 월간배치 작업이었다. 100만명이 넘는 사용자들을 처음 모수에 줄이기 위해서, 기획을 수정하거나 기술적으로 대상 모수를 줄이기 위해서, 차월에 조회했을때 동적으로 계산할수 있게 로직을 넣는 등 많은 보완 로직을 추가함에 따라서 비로서 나아질 수 있었다. 물론 시스템이라는 것이 QA 과정에서 많은 헛점이 발행해도 운영 환경에 따라서 달라지기 때문에 운영을 하면서도 많은 버그를 고쳤었지만, 이를 통해 많은 사용자들이 사용하는 시스템에서 어떻게하면 안정적인 시스템을 신규 과제를 접목할 수 있을지에 대한 고민을 많이 했었던 시간이었다.
Club
Club은 네이버 밴드같은 게시판 기능을 생각하면 되는데, 클럽에도 라이브 클럽의 팬클럽 같은 카테고리가 있다. 그리고 클럽에도 피드와 거의 비슷하지만 별도 서버로 분리되어 있었다. 여기선 펜레벨 시스템 개편하면서 팬클럽 수를 가져오는 작업을 진행했다. internal API로 인터페이스를 수행했었다.