<< 느린 것은 아름답다. | Home | 품질관리의 예방과 방어 전략에 대한 생각 >>

[번역] Urban Airship(Push 서비스)에서 C500k를 위해 벌인 일들

C500k in Action at Urban Airship

푸시 서버를 구축하는 분들이라면 Urban Airship 블로그를 관심있게 볼 필요가 있습니다. 푸시 서버 구축에 대한 시행착오나 튜닝 정보들을 포스팅하고 있어 도움이 될법한 콘텐츠들이 많습니다.
그 중 오늘은 "C500k in Action at Urban Airship"이란 아티클을 의역해 봅니다.

2000년쯤에는 c10k라는 문제(동시접속 만개를 해결하는 문제)가 주로 화두였는데, 현재는 c500k 도 이미 해결된 상태죠. Urban Airship의 경우를 본다면.. 물론 동접자수가 많은 것과 성능은 성질이 다르지만, 푸시같은 경우 지속적인 트래픽 처리도 중요하지만, 이벤트 캐치를 해 푸시 딜리버리 보장을 위해 영속성 있는 연결 구조의 특성을 가져가는 것도 중요해 보이네요.
그리고 이 글 다음으로 읽어야 할 것이 바로 "Linux Kernel Tuning for C500k"입니다. 커널 튜닝도 꼼꼼히 살펴봐야죠.

자, 그럼 내용으로 들어가 봅니다.

안드로이드용 AirMail 푸시 플랫폼 구축기

우리는 안드로이드용으로 AirMail Push 플랫폼을 구축중에 있고 이 플랫폼은 동시 접속 디바이스 수가 수백만을 처리할 수 있는 서버 사이드 인프라를 지원해야 한다. 애플의 iOS platform상의 푸시와 같이 안드로이드용 AirMail은 실시간으로 메세지를 푸시하기 위해서 서버와 클라이언트간에 연결 지향의 소켓 통신을 유지하고 있다.
이것은 서버 사이드에서 수십만 영속성 있는 소켓 통신 처리, 네트워크 끊기거나 네트워크에 재 연결된, 그리고 네트워크가 교환된 사용자의 경우처럼 클라이언트측의 연결 관리가 필요하고, 사용되지 않는 네트워크 감지하고 연결을 닫는 등의 복잡한 처리가 필요하다.

이 아티클은 서버 기반의 푸시 인프라 스트럭처를 구축하면서 직면했던 도전들을 설명한다. 어떻게 Java NIO 기반에 스레드/이벤트/큐 기반의 하이브리드형으로 재 설계하고 구현한 내용들이다.

많고 많은 소켓들

디바이스에 푸시 노티를 하기 위해서는 영속적인 소켓 연결을 가져가야 한다. 우리의 플랫폼은 디바이스의 동접이 수백만에 이르는 가용성이 필요했다. 그것은 서버 시스템의 스택과 소프트웨어는 각 노드가 수많은 동시 연결을 유지하면서 메세지를 받고 푸시 노티를 우아하게 수용할 필요가 있다. 중요한건 이들을 처리하는데 얼마나 많은 노드들이 필요한가?이다.
한 노드가 10,000 클라이언트를 지원한다면 벤처로서는 엄두도 못낼 비용을 감당해야 한다. 즉, 100만 디바이스를 수용할려면 100대의 서버가 필요하다는 이야기다.

초기에는 python의 Eventlet을 사용해 구현하면서 미지의 영역에서 벤처를 시작했다. Eventlet은 경량의 코루틴을 통해 병렬처리를 지원하는 환상적인 라이브러리다. Eventlet은 개발자들로 하여금 소켓 구현을 통해 대량의 접속을 쉽게 관리하고 수용할 수 있도록 해준다. 그리고 쓰레드 기반 서버와 달리 컨텍스트 전환이 쉽고 오버헤드가 적다. 그렇게 해서 Helium이라는 첫번째 엣지 서버가 탄생했다.

Eventlet기반의 초기 Helium 코드는 이해하기 쉽고 깨끗하고 심플했다. 우리는 Python도 좋았고, Eventlet 같은 라이브러리를 창출한 Python 커뮤니티도 마음에 들었다. Eventlet 기반 초기 Helium 성능은 EC2 하나의 인스턴스에서 사용할 메모리 1.7GB을 상한으로 설정하고 응용 프로그램 수준에서 keep-alive를 유지할 수 있는 조건으로 37,000 클라이언트를 수용했다. 숫자(성능)는 일단 만족하지만, 우리가 필요로하는 가용성을 수용하려면 많은 인스턴스가 필요로하게 된다. 이것은 돈이 많이 필요하다는 이야기가 된다.
우리는 성능적으로, 보안적으로, 때론 안정적인 플랫폼을 구축하기 위해 지속적으로 인프라스트럭처에 투자를 하고 있고, 우리는 인프라 관리자라기보다는 더 좋은 프로그래머들이고 싶고 성능과 비용 효율면에서 좋은 소프트웨어를 만들고 싶다.

바다 괴물처럼 엄청나 보이는 보이는 것들

우리는 (Eventlet기반의 초기 Helium보다) 매우 효율적인 소켓 서버 프로토타입을 구축하기 위해 탐구를 시작했다. 우리는 다양한 옵션들을 고려했다. C++기반의 [e]poll, 스레드/이벤트 기반 java나 scala구현, Node.js, 그외 다른 것들도 시도를 해봤다. 예상대로 순수 스레드 기반의 자바는 동시 연결 수가 수천대가 되면 쓸모없게 되고 C기반의 구현은 너무 저수준이어서 구현이 힘들고 프로젝트의 변화에 따라가지 못한다는 것이 판명되었다.

Java NIO(new/non-blocking IO) 라이브러리를 가지고 실험한 내용이 좋아서 아래의 세가지 경우를 가지고 다양하게 검증해 보았다.
  • 순수 자바 NIO 구현.
  • Netty에 NIO 얹은 구현.
  • Scala버전 Netty에 NIO 실은 구현.
위 세가지를 가지고 동시 연결 수와 메모리 효율성 관점에서 두 결과는 이전의 결과를 능가했다.

벤치 마크 1. 최대 동시 연결 수
구현안연결수메모리 사용량
Java Pure NIO512,000 +2.5 GB
Java w/Netty330,0002.2 GB
Scala w/Netty173,0001.5 GB

벤치 마크 2. 연결 당 메모리 효율성
구현안연결수메모리 사용량Delta
Java Pure NIO80,000 +581 MB1x
Java w/Netty80,000711 MB1.3x
Scala w/Netty80,000937 MB2.26x

열심히 하지 않았지만, 영리하게 작동했다.

EC2의 작은 인스턴스에 각 노드당 35,000개의 연결수였던 것이 512,000개까지 증가되었다. 이는 효율성, 인프라스트럭처 비용, 관리의 오버헤드 관점에서 크게 향상되었다. 각 노드당 동시 연결수는 Eventlet 기반 초기 Helium의 약 15배 향상되었다.
너무 일찍 터트린 샴페인일 수 있으나, NIO 플랫폼으로 구현된 우리의 애플리케이션 로직, keepalives, 메세지 통신을 구현한 뒤에서 전례없는 테스트 결과가 나왔다.

곁가지지만, Java+Netty나 Scala+Netty의 경우 드문 경우지만, Java 프로세스가 CPU 100%를 쳤는데도 콘솔에는 아무것도 출력되지 않았다. 이것은 예외상황에 노출되었으나 타이트한 무한 루프에서 예외상황을 방관하지 않았나 생각된다.(병목현상?)

이들 숫자를 바탕으로하여 우리는 Java + Pure NIO 구현을 밀어붙일 것이다. 그리고 계속 지켜봐주시기 바랍니다. 우리는 NIO구현과 관련된, 우리의 방법론이나 측정치에 대한 많은 정보들, 우리가 도중에 배운 것들, 우리가 피할수 있었지만, 곤란했던 상황들의 몇가지 포스트를 올릴 것이다.

Urban Airship의 푸시 아키텍처

참고로 Urban Airship의 아키텍처에 이해를 돕고자 추가한 단락이다. "Scaling Urban Airship’s Messaging Infrastructure to Light Up a Stadium in One Second" 아티클을 참고하여 작성했다.



  • Push Tag & Broadcast Service(codenamed Metalstorm) : 애플리케이션, 디바이스, 태그 사이의 연관 관계를 관리한다. 이 새로운 서비스는 매우 높은 푸시 처리량을 지원하고 수평적으로 확장 가능한 아키텍처를 통해 수억대 사용자를 가진 앱을 위해서 복잡한 태그 쿼리 수행을 지원한다.
  • Segments Data Storage(codenamed Penelope) : 고객의 위치 데이터를 포함한 공간 정보 질의에 최적화된 분산 데이터 베이스.
  • Message Routing Service(codenamed Gooey ButterCake) : 다양한 이기종 시스템을 엮어 질의를 통해 결과를 조합하기 위해서 소팅-머징-조인 알고리즘을 사용하는 라우팅 계층이다. 애플리케이션 태그와 디바이스 위치 정보를 조인하는 것이 한 예이다.
  • Edge Message Delivery Service(codenamed Yaw) : 높은 처리량과 낮은 지연 시간을 가진 APNS와 같은 3rd Party 푸시 프로바이더들에게 last-mile(푸시 수신 인프라와 디바이스 사이의 접점) 배달 처리를 한다. Yaw는 수십만의 연결 모두가 메세지 배달을 수행하도록 하는 TLS 협상, TTL 메세지, 프로토콜 준수 등을 관리한다.

참고 하세요.

아래 슬라이드는 Erik Onnen이 발표한 "High performance network programming on the jvm oscon 2012" 제목의 슬라이드인데, 도움될만한 내용들이 많습니다.



Re: Urban Airship(Push 서비스)에서 C500k를 위해 벌인 일들

좋은 글을 공유해주셔서 감사합니다 !

주변에 이런 주제로 밥먹고 사는 사람들이 다 없어져서, 다시 보니 반갑네요^^


Add a comment Send a TrackBack