푸시 시스템 개선

3 분 소요

모이고 서버 푸시 개선에 대한 포스팅

작성 이유

2019년 트위니에서 회사 시무식에서 이벤트로 모이고 그룹방에서 퀴즈에 대해 정답을 입력하는 이벤트를 진행했다.
해당 이벤트를 진행하던 도중 모이고 서버가 터지는 일이 발생했다. 심지어 모이고 서버는 Erlang으로 작성되어있다.
왜 이런 일이 발생한건지, 원인은 무엇이고 어떻게 이것을 개선했는지에 대한 글이다.


서버가 왜 터졌는가

서버가 터졌던 이유중 하나로는 서버가 1대만 떠있었기 때문이다.
서버가 1대만 떠있던 이유는 당시 모이고 DAU가 매우 적었기 때문에, 비용 절감을 위해 2대 -> 1대로 서버의 수를 줄여놨었다.
비용절감을 위해서라지만, 1대라면, 해당 서버가 죽어버리면, 바로 장애가 일어나게 된다. 애초에 2~3대를 기본적으로 운용했어야 했는데, 해당 부분은 우리팀의 실수였다.
이후, 서버는 상시 2대를 기본 운용하도록 변경하였다.
만약, 2대가 있었다면, 요청분산이 어느정도 될 테니, 서버가 죽진 않았을거라 생각된다.

서버가 터졌던 가장 큰 이유는 모이고 서버의 푸시 전송 방식이 효율적이지 않아서였다.
당시 클라이언트별로 데이터 푸시를 따로 보내고, 알람도 따로 보냈다.
채팅전송시 한명의 유저에게 보내는 푸시는 AOS의 채팅데이터 푸시. IOS 채팅 데이터 푸시. IOS 노티푸시가 있다. (클라이언트 타입별로 따로 보내고 있었다.)

만약, 그룹으로 채팅을 보낸다면, 개개인에게 푸시를 각자 전송했었고, 만약, 10명이 있는 방이라면 채팅 1개당 전송되는 푸시의 개수는 30개가 된다.
추가적으로 채팅을 읽었을때도 푸시가 있어서 이런걸 합하면, 50~60명의 유저가 채팅방에 있을 때, 일시에 채팅전송을 시작한다면, 어마어마한 양의 FCM 전송요청이 생긴다.
간단하게 계산해보면, 채팅 하나당 전송되는 푸시 요청 횟수는 150회가 되는데, 연속적으로 채팅을 전송하게된다면? 생각만해도 끔찍하다.

요청이 있는것만으로 서버가 죽은 이유는 Erlang에 동시에 떠있는 프로세스 개수가 많아지면서 메모리를 너무 많이 잡아먹어서였다.
만약, 채팅전송을 한다면, 푸시 자체는 비동기로 전송하기 때문에, 비동기 푸시를 위해 프로세스가 하나 더 뜬다. 이후, 보내야하는 유저별로 데이터 싱크를 위한 저장소에 추가적으로 데이터를 저장하게 된다. 해당 동작도 비동기로 동작한다. 즉 여기서 프로세스가 유저수만큼 뜨게 된다.
이후, lhttpc library 자체적으로 프로세스를 띄워서 요청했던 것으로 기억하는데, 해당 요청수만큼 다시 프로세스가 뜨고, 추가적으로 몇개의 프로세스가 더 뜨는 것을 확인했었다.

실제로, 개발서버에 테스트를 해봤었을때, 특정 프로세스가 많이 떠있던것을 확인했는데, ssl 관련 프로세스였던 것으로 기억한다.

위 과정을 거쳐 프로세스가 비정상적으로 많이 떠있게 되며, 메모리가 가득차게 되어 서버가 죽어버렸다.

그렇다면 해당 푸시를 어떻게 최적화를 시켜야할까? 라는 고민은 이전부터 있었다.
해당 현상에 대해 어느정도 예상했었고, 당시 사수형과 이야기해서 해당 부분에 대해 나중에 개선하자고 이야기가 되어있었다.

시무식 당시에 이러한 이유로 서버가 죽어버리자, 푸시 전송방식을 개선하였다.


푸시 방식 개선 여정

푸시 개선은 결과적으로 보면 아래와 같이 바뀌었다.

  개선이전 개선이후
토픽 유저토픽 유저토픽 + 그룹토픽 (그룹단위 토픽)
푸시방식 모든 유저에게 각자의 토픽에 대해 푸시 유저 대상으로는 이전과 동일한 방식으로 푸시. 하지만, 그룹이 대상이라면, 그룹 토픽으로 푸시

개선 이전 방법에서 개선 이후 방식으로 곧바로 넘어가게 되면 업데이트되지 않은 클라이언트들이 정상동작하지 않게 될것이고, 그렇다고 그대로 두면 서버가 또 터지는 일이 발생할테니, 양쪽을 모두 사용할 수 있는 방식으로 개선되었다.

일단, 푸시 데이터 종류에 따라, 우선순위를 지정을 하고, 우선순위에 따라 높은 우선순위의 아이템부터 유저에게 푸시하였다.
각 우선순위에 따라, AWS SQS에 push하고, 우선순위가 높은 큐에서부터 pull을 하여 우선 푸시하는 방법을 사용하였다.
우선순위가 높은 것들에는 채팅, 그룹원변경 등이 었었고, 낮은 운선순위의 것들은 채팅읽음 동기화 푸시 등이 있다.

그리고 여러개의 토픽에 대해서 묶어서 한번에 푸시할 수 있도록 FCM 요청도 개선되었다.

  ( ex condition : ~~ in topics || ~~ in topics)

해당 작업을 하고 난 이후에는 동일한 이유로 서버가 터지는 일은 없었다.

이후, 클라이언트가 업데이트를 어느정도 하였고, 개선버전 푸시 시스템으로 넘어가도 문제가 없는 시기가 되었다.


푸시 개선 적용

실제로 개선버전 푸시 방식을 적용하기 전, 클라이언트에 알람을 보내는 부분에서 문제가 발생하였다.
IOS의 경우, 백그라운드 동작을 할 수가 없어서 알람을 서버에서 직접 쏘는데, 유저 설정에 따라 알람을 받지 않기도 하고, 친구의 이름 설정에 따라, 알람이 다르게 와야 한다.
하지만, 코드 상에서는 대상 토픽을 대상으로 알람을 쏘고 있었기 때문에, 알람이 원하던 것과 다르게 전송되고 있었다.
따라서, 유저별로 알람을 따로 보내게끔 추가로 개선해야 했었다.

해당 부분에 대해 어떻게 해결할까 고민하며 서칭하던 중에 FCM 기능중 Multicast기능이 있고, 해당 기능이 알람을 보낼때 아주 적합하다고 생각했다.

그룹 알람의 경우, 알람을 꺼놓지 않은 유저들에 대한 정보만 가져와서 해당 유저들에게만 Notification을 푸시하도록 수정하였다.
개인 대상으로 전송해야 할 때는 차이가 없었지만, 그룹에서 여러명의 유저에게 한번에 묶어서 보낸다는 것은 대단히 큰 이점이라고 생각한다.
Notification에는 Multicast기능을 이용하여 푸시하도록 개선하였다.

또, 각 클라이언트 타입별로 메세지를 따로 보내고 있었는데, 현재 모이고에서 사용되고 있는 메세지 body는 IOS/ANDROID를 따로 보낼 필요가 없어서 하나로 통합하여 보내도록 개선하였다.

개선이 모두 이루어 지고 나서, 너무나도 느렸던 모이고의 푸시 속도는 놀라울 정도로 개선되었다.

많은 부분이 좋아지고 있지만, 갈길은 여전히 멀다.


추가적으로 해당 작업도중 HTTP v1이 출시된 것을 알고 버전을 올리려고 했으나, 버그가 발생하여 올리지 못했다. 해당 부분에 대해서는 추후에 따로 포스팅해볼 예정이다.

카테고리:

업데이트: