node.js, Mongodb, Redis, การลดลงของประสิทธิภาพของ Ubuntu ในการผลิต, RAM ฟรี, CPU 100%


11

ตามที่ชื่อคำถามแสดงว่าฉันมีเวลายากที่จะทราบว่าสามารถปรับปรุงอะไรในแอปพลิเคชันของฉัน (หรือปรับในระบบปฏิบัติการ, Ubuntu) เพื่อให้ได้ประสิทธิภาพที่ยอมรับได้ แต่ก่อนอื่นฉันจะอธิบายสถาปัตยกรรม:

เซิร์ฟเวอร์ front-end เป็นเครื่อง 8 คอร์ที่มี 8 gigs RAM ใช้งาน Ubuntu 12.04 แอปพลิเคชั่นเขียนทั้งหมดใน javascript และทำงานใน node.js v 0.8.22 (เนื่องจากโมดูลบางตัวดูเหมือนจะบ่นกับโหนดเวอร์ชั่นใหม่กว่า) ฉันใช้ nginx 1.4 ถึงปริมาณการรับส่งข้อมูลพร็อกซี HTTP จากพอร์ต 80 และ 443 ถึง 8 คนทำงานโหนดที่จัดการ และเริ่มใช้โหนดคลัสเตอร์ api ฉันใช้ socket.io เวอร์ชันล่าสุด 0.9.14 เพื่อจัดการการเชื่อมต่อ websocket ซึ่งฉันได้เปิดใช้งานเฉพาะ websockets และ xhr-polling เป็นการขนส่งที่มีอยู่ ในเครื่องนี้ฉันยังใช้งาน Redis (2.2)

ฉันเก็บข้อมูลถาวร (เช่นผู้ใช้และคะแนน) บนเซิร์ฟเวอร์ตัวที่สองบน mongodb (3.6) ด้วย 4gigs RAM และ 2 คอร์

แอพนี้กำลังใช้งานมาตั้งแต่ไม่กี่เดือน (มันทำงานบนกล่องเดียวจนกระทั่งไม่กี่สัปดาห์ที่ผ่านมา) และมันถูกใช้งานโดยผู้ใช้ประมาณ 18k ต่อวัน มันทำงานได้ดีมากนอกเหนือจากปัญหาหลักอย่างหนึ่งคือประสิทธิภาพลดลง ด้วยการใช้งานปริมาณของซีพียูที่ใช้โดยแต่ละกระบวนการจะเพิ่มขึ้นเรื่อย ๆ จนกว่ามันจะเป็นตัวกำหนดคนงาน (ซึ่งจะไม่ให้บริการตามคำขออีกต่อไป) ฉันได้แก้ไขมันชั่วคราวเพื่อตรวจสอบ cpu ที่ใช้งานโดยผู้ปฏิบัติงานแต่ละคนทุกนาทีและเริ่มใหม่หากถึง 98% ดังนั้นปัญหาที่นี่ส่วนใหญ่เป็นซีพียูและไม่ใช่แรม RAM ไม่ใช่ปัญหาอีกต่อไปตั้งแต่ฉันอัพเดตเป็น socket.io 0.9.14 (เวอร์ชั่นก่อนหน้านี้มีหน่วยความจำรั่ว) ดังนั้นฉันสงสัยว่ามันจะเป็นปัญหาหน่วยความจำรั่วโดยเฉพาะอย่างยิ่งเพราะตอนนี้มันเป็นซีพียูที่เติบโตอย่างรวดเร็ว ฉันต้องรีสตาร์ตแต่ละคนประมาณ 10-12 ครั้งต่อวัน!) RAM ที่ใช้งานเพิ่มขึ้นเช่นกัน แต่ช้ามาก 1 กิ๊กทุก 2-3 วันของการใช้งานและสิ่งที่แปลกคือมันไม่ได้ถูกปล่อยออกมาแม้ว่าฉันจะรีสตาร์ทแอพพลิเคชั่นทั้งหมด มันจะเปิดตัวก็ต่อเมื่อฉันรีบูตเซิร์ฟเวอร์! นี่ฉันไม่เข้าใจจริงๆ ...

ตอนนี้ฉันได้ค้นพบnodeflyซึ่งเป็นสิ่งที่น่าอัศจรรย์ดังนั้นในที่สุดฉันก็สามารถเห็นสิ่งที่เกิดขึ้นบนเซิร์ฟเวอร์การผลิตของฉันและฉันกำลังรวบรวมข้อมูลตั้งแต่สองสามวัน หากใครต้องการดูแผนภูมิฉันสามารถให้คุณเข้าถึงได้ แต่โดยทั่วไปฉันเห็นว่าฉันมีการเชื่อมต่อระหว่าง 80 และ 200 พร้อมกัน! ฉันคาดหวังว่า node.js จะจัดการกับคำขอนับพันไม่ใช่คำขอหลายร้อยรายการ นอกจากนี้เวลาตอบสนองเฉลี่ยสำหรับปริมาณการใช้ http จะอยู่ระหว่าง 500 ถึง 1,500 มิลลิวินาทีซึ่งฉันคิดว่ามันเยอะมาก นอกจากนี้ในขณะนี้ที่มีผู้ใช้ 1,300 คนออนไลน์นี่คือผลลัพธ์ของ "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

ซึ่งแสดงให้เห็นว่าฉันมีการเชื่อมต่อที่ปิดเป็นจำนวนมากใน timewait ฉันได้เพิ่มไฟล์ที่เปิดสูงสุดเป็น 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 ซึ่งด้วยเหตุผลบางอย่างทำให้พอร์ต / ซ็อกเก็ตที่มีอยู่ (?) มี แต่สิ่งหนึ่งที่ไม่สมเหตุสมผลสำหรับฉัน: ทำไมเมื่อฉันรีสตาร์ทพนักงานและลูกค้าทั้งหมดเชื่อมต่อใหม่ภายในไม่กี่วินาที โหลดซีพียูของผู้ปฏิบัติงานลดลงเหลือ 1% และสามารถให้บริการคำขอได้อย่างถูกต้องจนกว่ามันจะอิ่มตัวหลังจากผ่านไปประมาณ 1 ชั่วโมง (ในช่วงเวลาเร่งด่วน)?

ฉันเป็นโปรแกรมเมอร์จาวาสคริปต์ส่วนใหญ่ไม่ใช่ผู้ดูแลระบบ sys ดังนั้นฉันจึงไม่ทราบว่าฉันควรคาดหวังว่าจะรับภาระเท่าใดกับเซิร์ฟเวอร์ของฉัน แต่แน่นอนว่ามันไม่ทำงานตามที่ควร แอพพลิเคชั่นมีความเสถียรเป็นอย่างอื่นและปัญหาสุดท้ายนี้ทำให้ฉันไม่สามารถจัดส่งแอพเวอร์ชั่นมือถือที่พร้อมใช้งานได้เพราะเห็นได้ชัดว่าพวกเขาจะโหลดมากขึ้น

หวังว่าจะมีบางสิ่งที่ชัดเจนว่าฉันทำผิดและใครบางคนจะช่วยชี้ให้เห็น ... อย่าลังเลที่จะขอข้อมูลเพิ่มเติมและขออภัยในความยาวของคำถาม แต่จำเป็นฉันเชื่อว่า ... ขอบคุณล่วงหน้า!


มีวิธีใดบ้างที่จะได้อะไรเช่นเธรดการถ่ายโอนข้อมูลจาก node.js? อาจมีบางเธรดในการวนซ้ำไม่สิ้นสุด นอกจากนี้สิ่งที่ใช้ cpu จริง? สิ่งใดที่คุณเห็นtopเมื่อใช้ cpu ใกล้ 100%
rvs

cpu ถูกใช้ทั้งหมดโดย nodejs เมื่อฉันเรียกใช้บนฉันเห็นกระบวนการโหนดรับ cpu ทั้งหมด ไม่แน่ใจว่าฉันสามารถเอาท์พุทการถ่ายโอนข้อมูลด้ายจากโหนดจะซื่อสัตย์ ...
Franjanko

อีกสิ่งหนึ่งที่ชี้ให้เห็นว่าเวลาส่วนใหญ่ของ CPU ดูเหมือนว่าจะไปที่ระบบไม่ใช่เวลาของผู้ใช้
Franjanko

อย่างน้อยทุกคนรู้ว่าฉันสามารถเชื่อมต่อพร้อมกันจำนวนมากกับเซิร์ฟเวอร์ที่ฉันมี ในขณะนี้ฉันรองรับการเชื่อมต่อพร้อมกันสูงสุด 200 การเชื่อมต่อ สิ่งนี้จะช่วยให้ฉันประเมินว่าฉันอยู่ไกลจากการกำหนดค่าที่เหมาะสมที่สุด ... ขอบคุณ
Franjanko

คำตอบ:


10

หลังจากสองสามวันของการทดลองและข้อผิดพลาดที่รุนแรงฉันดีใจที่ได้พูดว่าฉันเข้าใจว่าคอขวดอยู่ที่ไหนและฉันจะโพสต์ที่นี่เพื่อให้ผู้อื่นได้รับประโยชน์จากสิ่งที่ฉันค้นพบ

ปัญหาอยู่ที่การเชื่อมต่อ pub / sub ที่ฉันใช้กับ socket.io และโดยเฉพาะอย่างยิ่งใน RedisStore ที่ใช้โดย socket.io เพื่อจัดการการสื่อสารระหว่างกระบวนการของซ็อกเก็ตอินสแตนซ์

หลังจากทราบว่าฉันสามารถติดตั้ง pub / sub ของฉันเองโดยใช้ redis ได้อย่างง่ายดายฉันตัดสินใจลองและลบ redisStore ออกจาก socket.io ทิ้งไว้กับที่เก็บหน่วยความจำเริ่มต้น (ฉันไม่ต้องการออกอากาศไปที่ ไคลเอนต์ที่เชื่อมต่อทั้งหมด แต่ระหว่าง 2 ผู้ใช้ที่แตกต่างกันอาจเชื่อมต่อบนกระบวนการที่แตกต่างกัน

เริ่มแรกฉันประกาศกระบวนการเชื่อมต่อ redis ทั่วโลกเพียง 2 กระบวนการเพื่อจัดการ pub / sub บนไคลเอนต์ที่เชื่อมต่อทุกครั้งและแอปพลิเคชันใช้ทรัพยากรน้อยลง แต่ฉันยังคงได้รับผลกระทบจากการใช้งาน CPU ที่เพิ่มขึ้นเรื่อย ๆ แต่จากนั้นฉันตัดสินใจที่จะลองสร้างการเชื่อมต่อใหม่ที่ 2 เพื่อ redis สำหรับไคลเอ็นต์แต่ละรายเพื่อจัดการ pub / sub ของพวกเขาเฉพาะในเซสชันของพวกเขาแล้วปิดการเชื่อมต่อเมื่อผู้ใช้ยกเลิกการเชื่อมต่อ หลังจากหนึ่งวันของการใช้งานในการผลิตซีพียูยังคงอยู่ที่ 0-5% ... บิงโก! ไม่มีการรีสตาร์ทกระบวนการไม่มีข้อบกพร่องพร้อมประสิทธิภาพที่ฉันคาดหวัง ตอนนี้ฉันสามารถพูดได้ว่า node.js โขดหินและมีความสุขที่ได้เลือกมันสำหรับการสร้างแอพนี้

โชคดีที่ Redis ได้รับการออกแบบมาเพื่อรองรับการเชื่อมต่อพร้อมกันจำนวนมาก (แตกต่างกันโดย Mongo) และโดยค่าเริ่มต้นมันถูกตั้งค่าไว้ที่ 10k ที่ออกจากห้องสำหรับผู้ใช้พร้อมกันประมาณ 5k บนอินสแตนซ์ Redis เดียวซึ่งเพียงพอสำหรับฉัน ฉันได้อ่านแล้วว่าสามารถเชื่อมต่อได้พร้อมกันสูงสุด 64k ดังนั้นสถาปัตยกรรมนี้ควรจะแข็งแกร่งพอที่ฉันเชื่อ

ณ จุดนี้ฉันคิดว่าจะนำพูลการเชื่อมต่อบางประเภทไปใช้กับ Redis อีกครั้งเพื่อปรับให้เหมาะสมอีกเล็กน้อย แต่ไม่แน่ใจว่าจะไม่ทำให้เกิดเหตุการณ์ pub / sub อีกครั้งเพื่อสร้างการเชื่อมต่อ ถูกทำลายและสร้างใหม่ทุกครั้งเพื่อทำความสะอาด

อย่างไรก็ตามขอบคุณสำหรับคำตอบของคุณและฉันจะอยากรู้ว่าคุณคิดอย่างไรและหากคุณมีข้อเสนอแนะอื่น ๆ

ไชโย


2
ฉันมีสิ่งที่ดูเหมือนจะเป็นปัญหาเดียวกันในแอปที่ใช้งานจริงของฉันซึ่งเป็นเรื่องใหม่สำหรับบทบาทผู้ดูแลระบบเซิร์ฟเวอร์ ฉันติดตามสิ่งที่คุณทำในแนวคิด แต่ฉันมีคำถามบางอย่างเกี่ยวกับวิธีการทำ - บางทีคุณอาจให้ลิงก์ไปยังแหล่งข้อมูลบางอย่างในคำตอบที่คุณยอมรับ? หรือเพียงแค่ให้ข้อมูลเพิ่มเติม โดยเฉพาะอย่างยิ่งเกี่ยวกับ "แต่แล้วฉันตัดสินใจที่จะลองสร้างการเชื่อมต่อใหม่ 2 อันเพื่อ redis สำหรับลูกค้าแต่ละรายเพื่อจัดการ pub / sub ของพวกเขาเฉพาะในเซสชันของพวกเขาแล้วปิดการเชื่อมต่อเมื่อผู้ใช้ยกเลิกการเชื่อมต่อ"
toblerpwn

2

คุณมีซอร์สโค้ดที่จะถ่ายโอนข้อมูลบ้างไหม? มันอาจเป็นการเชื่อมต่อกับฐานข้อมูลไม่ได้ปิด? กระบวนการกำลังรอการเชื่อมต่อ HTTP ที่ไม่เคยปิด

คุณสามารถโพสต์บันทึกบางส่วนได้หรือไม่

ทำ ps -ef และตรวจสอบว่าไม่มีอะไรยังทำงานอยู่ ฉันเคยเห็นกระบวนการทางเว็บทำให้ซอมบี้ไม่ตายจนกว่าคุณจะฆ่า -9 บางครั้งการปิดระบบไม่ทำงานหรือไม่ทำงานอย่างสมบูรณ์และเธรดหรือกระบวนการเหล่านั้นจะถือ RAM และบางครั้ง CPU

อาจเป็นวงไม่สิ้นสุดที่ใดที่หนึ่งในรหัสหรือกระบวนการขัดข้องที่ถือ ontop การเชื่อมต่อฐานข้อมูล

โมดูล 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

ไม่ใช่คำตอบต่อคำถามของคุณเป็นคำถามมากกว่าคำถามแบบตอบรับหนึ่งข้อ

เพียงแค่บอกว่าฉันสร้างเซิร์ฟเวอร์ node.js สำเร็จด้วย socket.io จัดการการเชื่อมต่อแบบถาวรมากกว่า 1 ล้านครั้งโดยมีค่าเฉลี่ยข้อความ 700 ไบต์

การ์ดเชื่อมต่อเครือข่ายที่ 1Gbps เริ่มอิ่มตัวและฉันก็เห็น I / O รออยู่จำนวนมากจากการเผยแพร่กิจกรรมไปยังลูกค้าทั้งหมด

การลบ nginx ออกจากบทบาทพร็อกซียังส่งคืนหน่วยความจำอันมีค่าด้วยเนื่องจากการเข้าถึงการเชื่อมต่อที่มีอยู่ถึงหนึ่งล้านครั้งด้วยเซิร์ฟเวอร์เพียงเครื่องเดียวนั้นเป็นงานที่ยากลำบากสำหรับการปรับแต่งค่าแอพพลิเคชั่น โปรดจำไว้ว่ามันสามารถใช้งานได้กับ RAM จำนวนมากเท่านั้น (การเชื่อมต่อ websockets ประมาณ 1M กิน RAM ขนาด 16GB ด้วย node.js ฉันคิดว่าการใช้ sock.js จะเหมาะสำหรับการใช้หน่วยความจำต่ำ แต่ตอนนี้ socket.io กินมาก)

ลิงค์นี้เป็นจุดเริ่มต้นของฉันในการเข้าถึงปริมาณการเชื่อมต่อกับโหนด นอกจากนี้ยังเป็นแอพ Erlang การปรับแต่งระบบปฏิบัติการทั้งหมดเป็นแอพพลิเคชั่นที่ไม่เชื่อเรื่องพระเจ้าและควรจะมีการใช้งานโดยทุกคนที่มีจุดมุ่งหมายในการเชื่อมต่อแบบถาวร (websockets หรือโพลยาว)

HTH,

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.