node.js, mongodb, redis, 프로덕션에서 우분투 성능 저하, RAM이 없음, CPU 100 %


11

질문 제목에서 알 수 있듯이 수용 가능한 성능을 달성하기 위해 응용 프로그램에서 개선 될 수있는 것을 파악하기가 어렵습니다 (또는 os, 우분투에서 조정). 그러나 먼저 아키텍처를 설명하겠습니다.

프론트 엔드 서버는 Ubuntu 12.04를 실행하는 8 기가 RAM이있는 8 코어 시스템입니다. 응용 프로그램은 완전히 자바 스크립트로 작성되고 node.js v 0.8.22에서 실행됩니다 (일부 모듈은 최신 버전의 노드에서 불평하는 것처럼 보입니다) nginx 1.4를 사용하여 관리되는 포트 80 및 443에서 8 노드 작업자에게 http 트래픽을 프록시 처리합니다. 노드 클러스터 API를 사용하기 시작했습니다. 웹 소켓 연결을 처리하기 위해 최신 버전의 socket.io 0.9.14를 사용합니다. 웹 소켓 및 xhr-polling 만 사용 가능한 전송으로 활성화했습니다. 이 머신에서도 Redis (2.2) 인스턴스를 실행합니다

4gigs RAM과 2 코어로 mongodb (3.6)의 두 번째 서버에 사용자 및 점수와 같은 영구 데이터를 저장합니다.

이 앱은 몇 달 (몇 주 전까지 단일 상자에서 실행 중)부터 운영되고 있으며 하루에 약 18k 명의 사용자가 사용하고 있습니다. 성능 저하라는 한 가지 주요 문제와는 별개로 항상 잘 작동했습니다. 사용하면 각 프로세스에서 사용하는 CPU의 양이 작업자를 더 이상 요청하지 않을 때까지 증가합니다 (더 이상 요청을 처리하지 않음). 매 분마다 각 작업자가 사용중인 CPU를 확인하고 98 %에 도달하면 다시 시작하여 일시적으로 해결했습니다. 따라서 여기서 문제는 주로 CPU가 아니라 RAM입니다. socket.io 0.9.14 (이전 버전은 메모리 누수)로 업데이트 한 이후 RAM이 더 이상 문제가되지 않으므로 메모리 누수 문제가 의심됩니다. 특히 CPU가 상당히 빠르게 커지기 때문에 특히 나는 하루에 약 10-12 번 각 근로자를 다시 시작해야합니다!). 사용중인 RAM이 정직하게 커지고 그러나 매우 느리게, 2-3 일마다 1 기가 사용되며, 이상한 점은 전체 응용 프로그램을 완전히 다시 시작해도 해제되지 않는다는 것입니다. 서버를 재부팅하면 해제됩니다! 이건 정말 이해가되지 않습니다 ...

이제 놀라운 nodefly 를 발견 하여 프로덕션 서버에서 무슨 일이 일어나고 있는지 확인할 수 있으며 며칠 후에 데이터를 수집하고 있습니다. 누구나 차트를보고 싶다면 액세스 권한을 부여 할 수 있지만 기본적으로 80 ~ 200 개의 동시 연결이 있음을 알 수 있습니다! node.js가 수백 건의 요청이 아닌 수천 건을 처리 할 것으로 기대했습니다. 또한 http 트래픽의 평균 응답 시간은 500 ~ 1500 밀리 초 사이이며 실제로는 많이 생각합니다. 또한 현재 1300 명의 사용자가 온라인 상태 인 순간, 이것은 "ss -s"의 출력입니다.

Total: 5013 (kernel 5533)
TCP:   8047 (estab 4788, closed 3097, orphaned 139, synrecv 0, timewait 3097/0), ports 0

Transport Total     IP        IPv6
*         5533      -         -
RAW       0         0         0
UDP       0         0         0
TCP       4950      4948      2
INET      4950      4948      2
FRAG      0         0         0

이것은 시간 대기 상태에서 많은 닫힌 연결이 있음을 보여줍니다. 최대 열린 파일을 999999로 늘 렸습니다. 여기에 ulimit -a의 출력이 있습니다.

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 63724
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 63724
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

그래서 문제가 http 트래픽에 어떤 이유로 사용 가능한 포트 / 소켓 (?)을 포화시킬 수 있다고 생각했지만 한 가지 이해가되지 않습니다. 왜 작업자를 다시 시작하고 모든 클라이언트가 몇 초 내에 다시 연결되는지, 작업자의 CPU 부하가 1 %로 내려 가고 약 1 시간 (피크 타임) 후에 포화 될 때까지 요청을 제대로 처리 할 수 ​​있습니까?

나는 주로 sys 관리자가 아닌 자바 스크립트 프로그래머이므로 서버로 처리 해야하는 부하가 얼마인지는 모르지만 반드시 제대로 작동하지는 않습니다. 응용 프로그램은 그렇지 않으면 안정적 이며이 마지막 문제는 준비가 된 모바일 버전의 응용 프로그램을 배송하지 못하게합니다. 분명히 더 많은로드를 가져 와서 결국 모든 것을 중단시킬 것입니다!

바라건대 내가 잘못하고있는 것이 분명하고 누군가가 그것을 발견하는 데 도움이 될 것입니다 ... 자세한 정보를 요청하십시오. 질문의 길이는 미안하지만 필요하다고 생각했습니다 ... 미리 감사드립니다!


node.js에서 스레드 덤프와 같은 것을 얻을 수있는 방법이 있습니까? 무한 루프에는 약간의 스레드가있을 수 있습니다. 또한 실제로 CPU를 사용하는 것은 무엇입니까? topCPU 사용량이 100 %에 가까울 때 무엇을 보십니까?
RVS

CPU는 nodejs가 전적으로 사용합니다. 맨 위를 실행하면 노드 프로세스가 모든 CPU를 사용하는 것을 볼 수 있습니다. 어떻게 노드에서 스레드 덤프를 출력 할 수 있는지 잘 모르겠습니다 ...
Franjanko

지적해야 할 또 다른 점은 CPU 시간의 대부분이 사용자 시간이 아닌 시스템으로 이동하는 것 같습니다
Franjanko

적어도 내가 가지고있는 서버로 처리 할 수있는 동시 연결 수를 아는 사람이 있습니까? 현재 최대 200 개의 동시 연결을 지원합니다. 이것은 내가 최적의 구성에서 얼마나 멀리 떨어져 있는지 추정하는 데 도움이 될 것입니다 ... 감사합니다.
Franjanko

답변:


10

며칠간의 격렬한 시행 착오 끝에 병목 현상이 발생한 위치를 이해했다고 말할 수있어 기쁘게 생각하며 다른 사람들이 내 연구 결과를 활용할 수 있도록 여기에 게시하겠습니다.

문제는 socket.io와 함께 사용했던 pub / sub 연결, 특히 socket.io가 소켓 인스턴스의 프로세스 간 통신을 처리하는 데 사용하는 RedisStore에 있습니다.

redis를 사용하여 내 자신의 pub / sub 버전을 쉽게 구현할 수 있음을 깨닫고 나서 시도해보기로 결정하고 socket.io에서 redisStore를 제거하여 기본 메모리 저장소로 남겨 두었습니다 (방송 할 필요가 없습니다) 연결된 모든 클라이언트 (다른 ​​프로세스에서 연결된 두 명의 다른 사용자 만 가능)

처음에는 연결된 모든 클라이언트에서 pub / sub를 처리하기위한 2 개의 전역 redis 연결 x 프로세스 만 선언했으며 응용 프로그램은 덜 사용하지만 여전히 CPU 사용량 증가의 영향을 받았으므로 크게 변경되지 않았습니다. 그러나 각 클라이언트가 세션에서만 pub / sub를 처리 할 수 ​​있도록 redis에 대해 2 개의 새로운 연결을 작성하기로 결정한 다음 사용자의 연결이 끊어지면 연결을 닫으십시오. 생산에서 하루를 사용한 후에도 CPU는 여전히 0-5 %였습니다 ... 빙고! 내가 기대했던 성능으로 프로세스가 다시 시작되거나 버그가 발생하지 않습니다. 이제 node.js가 흔들리고이 앱을 만들기 위해 그것을 선택하게되어 기쁩니다.

다행히도 redis는 많은 동시 연결 (몽고에 따라 다름)을 처리하도록 설계되었으며 기본적으로 10k로 설정되어 약 5k 명의 동시 사용자를위한 공간을 단일 redis 인스턴스에 남겨 두었습니다. ve는 최대 64k 동시 연결로 푸시 할 수 있으므로이 아키텍처는 충분히 견고해야한다고 생각합니다.

이 시점에서 나는 일종의 연결 풀을 redis에 구현하여 조금 더 최적화하기를 원했지만 각각의 경우가 아니라면 pub / sub 이벤트가 연결에 다시 발생하지 않을지 확실하지 않습니다. 청소하기 위해 매번 파괴되고 재생됩니다.

어쨌든, 당신의 답변에 감사드립니다. 나는 당신이 어떻게 생각하는지, 그리고 다른 제안이 있는지 궁금합니다.

건배.


2
프로덕션 응용 프로그램에서 동일한 문제로 보이는데 서버 관리자 역할에도 새로운 것이 있습니다. 나는 당신이 개념적으로 한 일을 따르지만 어떻게 해야하는지에 대해 몇 가지 질문이 있습니다. 아마도 당신은 당신의 수락 된 답변에 일부 리소스에 대한 링크를 제공 할 수 있습니까? 아니면 단순히 더 많은 정보를 제공 하시겠습니까? 특히 "하지만 각 클라이언트가 세션에서만 pub / sub를 처리 할 수 ​​있도록 redis에 대해 2 개의 새로운 연결을 작성하기로 결정한 후 사용자의 연결이 끊어지면 연결을 닫습니다."
toblerpwn

2

덤프 할 소스 코드가 있습니까? 데이터베이스에 대한 연결이 닫히지 않았을 수 있습니까? 절대 닫히지 않는 HTTP 연결을 기다리는 프로세스.

로그를 게시 할 수 있습니까?

ps -ef를 수행하고 여전히 실행중인 것이 없는지 확인하십시오. 나는 웹 프로세스가 당신이 kill -9 할 때까지 죽지 않는 좀비를 남기는 것을 보았습니다. 때때로 시스템 종료가 작동하지 않거나 완전히 작동하지 않고 이러한 스레드 또는 프로세스가 RAM과 CPU를 보유하게됩니다.

코드 어딘가에 무한 루프이거나 db 연결을 유지하는 충돌 프로세스 일 수 있습니다.

어떤 NPM 모듈을 사용하고 있습니까? 그들은 모두 최신입니까?

예외가 발생합니까? 참조 : http://geoff.greer.fm/2012/06/10/nodejs-dealing-with-errors/ 참조 : /programming/10122245/capture-node-js-crash-reason

일반적인 팁 :

http://clock.co.uk/tech-blogs/preventing-http-raise-hangup-error-on-destroyed-socket-write-from-crashing-your-nodejs-server

http://blog.nodejitsu.com/keep-a-nodejs-server-up-with-forever

http://hectorcorrea.com/blog/running-a-node-js-web-site-in-production-a-beginners-guide

/programming/1911015/how-to-debug-node-js-applications

https://github.com/dannycoates/node-inspector

http://elegantcode.com/2011/01/14/taking-baby-steps-with-node-js-debugging-with-node-inspector/


1

귀하의 질문은 단답형 지적 질문보다 단순한 이야기이므로 답변 자체는 아닙니다.

메시지 페이로드 평균 700 바이트로 1 백만 개가 넘는 영구 연결을 처리하는 socket.io로 node.js 서버를 성공적으로 구축했다고 말하면됩니다.

처음에 1Gbps의 네트워크 인터페이스 카드가 포화 상태 였고 게시 이벤트에서 모든 클라이언트로의 많은 I / O 대기 시간이 나타났습니다.

프록시 역할에서 nginx를 제거하면 귀중한 메모리가 반환됩니다. 하나의 서버로만 백만 개의 영구 연결에 도달하는 것은 구성, 응용 프로그램 및 OS 매개 변수를 조정하는 힘든 작업이기 때문입니다. 그것은 많은 RAM으로 만 가능하다는 것을 명심하십시오 (1M 웹 소켓 연결은 약 16GB의 RAM을 사용합니다. node.js와 함께, sock.js를 사용하는 것은 메모리 소비가 적지 만 지금은 socket.io에 이상적이라고 생각합니다 많이 소비합니다).

이 링크 는 노드와의 해당 연결 볼륨에 도달하기위한 시작점이었습니다. Erlang 앱일뿐 아니라 모든 OS 튜닝은 거의 응용 프로그램에 구애받지 않으며 많은 지속적인 연결 (웹 소켓 또는 긴 폴링)을 목표로하는 모든 사람이 사용해야합니다.

HTH,

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.