2012-04-21
Node.js로 25만 동접 만들기
via caustik's blog
node.js 를 10만 동접으로 스케일링하기
- 10만 동접을 처리하는데 CPU 점유율 40%, (virtual) 메모리 1.4G 를 차지함. 이 정도면 rackspace 2G 클라우드 서버일 경우 시간당 0.1$ 로 가능함.
- 각 연결은 5초에 한번 메시지를 보냄. 대략 초당 4만개의 JSON 패킷이 보내짐. 응답성도 좋음.
이런 성능을 위한 설정은:
- Nagle 알고리즘을 사용안함: 빠른 응답성을 위해 커널 내부에 네트워크 버퍼링을 사용하지 않고 즉시 보내지도록 함. socket.setNoDelay().
- v8의 idle 가비지 컬렉션을 끄기 위해 --nouse-idle-notification 옵션 사용: JS 객체가 2백만개 정도 되면 몇 초마다 가비지 컬렉션 때문에 1초 가까이 랙이 걸림. 실제로 객체들을 순회하는 데만 이 정도의 시간이 소모됨.
[역주] 소스 코드를 보면 --expose_gc 옵션으로 실행해서 gc()를 실행할 수 있게 한 다음, /debug/gc 메시지를 보내서 가비지 컬렉션을 하게 되는데, 이걸 실행하는 순간 랙은 뭘 해도 피할 수 없음. ㅠㅠ
node.js 로 25만 동접 만들기
-
가장 최근 태그된 v8 리비전의 성능이 그나마 낫다.
-
25만 연결은 v8의 1.4기가 힙 메모리 제한 하에서의 최대치. 이 상황에서도 CPU 사용량이나 메모리 사용량이 낮은 걸 본다면, 충분히 더 나아질 수 있을 듯.
-
여러 번 테스트해본 결과, SVN의 가장 최근에 태그된 리비전이 그나마 제일 안정적이었음
-
클러스터 모듈의 워커들을 이용
-
100개의 아마존 EC2 서버들로부터 초당 10만개의 JSON HTTP GET 요청을 보냄.
- 클러스터 모듈을 사용해서 일시적인 요청(?) 처리의 오버헤드를 줄임. (35만 연결 중 10만개 정도가 일시적이었다) [역주] 자식 프로세스로 어떻게 뭘 분산하는지는 현재 미확인
- 마스터는 이전과 동일. 워커는 CPU 당 하나씩 생성하고, 마스터와는 다른 포트를 사용해서 요청을 받아서 마스터로 포워딩함. 이렇게 한 이유는 오직 1.4기가 힙 제한 때문.
V8의 1.4GB 힙제한 벗어나기
ulimit -n 999999
: 소켓 오픈 개수 제한을 증가. 기본 1024.--nouse-idle-notification
: 가바지 컬렉터가 자동적으로 실행되는 걸 막음. 30초마다 4초짜리 랙이 걸리고 싶지 않으면 사용해야 함. --expose-gc 로 gc() 함수를 자바스크립트에서 직접 호출.--max-old-space-size=8192
: 메모리 제한 최대값을 임의로 설정.- 최신 v8 소스를 약간 고쳐서 빌드: 메모리 관련 설정이나 가비지 컬렉터 실행 부분을 수정.
최근엔 1.4G 제한을 넘어서 2.2G 까지도 사용할 수 있었음. :)
[역주] node.js 서버 실행시 넘기는 파라미터:
node --trace-gc --expose-gc --nouse-idle-notification --max-new-space-size=2048 --max-old-space-size=8192 sprites.js
총평
- 게임 서버를 개발시 이슈가 되는, 응답성, 동접 처리 등의 문제를 node.js 개발자들도 만나기 시작함. 의외로 메모리 제한이 문제가 된다는 게 특이했음. node.js 개발진은 메모리 제약은 클러스터 모듈로 분산해서 해결하는 걸 추천하는 듯.
- 가비지 컬렉팅 문제는 응답성이 중요한 액션 게임을 개발할 경우 심각할 수 있음. (4초나 랙이 걸린다니! 그것도 30초마다!) 그래도 소셜 게임 수준은 걱정하지 않아도 될 듯.