node.js, mongodb, redis, suy giảm hiệu năng của Ubuntu trong sản xuất, RAM miễn phí, CPU 100%


11

Như tiêu đề câu hỏi cho thấy, tôi đang gặp khó khăn để tìm ra những gì có thể được cải thiện trên ứng dụng của mình (hoặc điều chỉnh trong hệ điều hành, ubfox) để đạt được hiệu suất chấp nhận được. Nhưng trước tiên tôi sẽ giải thích kiến ​​trúc:

Máy chủ ngoại vi là một máy 8 lõi với 8 gigs RAM chạy Ubuntu 12.04. Ứng dụng này được viết hoàn toàn bằng javascript và chạy trong node.js v 0.8.22 (vì một số mô-đun dường như phàn nàn trên các phiên bản mới hơn của nút) Tôi sử dụng nginx 1.4 để lưu lượng truy cập http từ cổng 80 và 443 đến 8 nhân viên nút được quản lý và bắt đầu sử dụng api cụm nút. Tôi sử dụng phiên bản mới nhất của socket.io 0.9.14 để xử lý các kết nối websocket, trên đó tôi chỉ kích hoạt websockets và xhr-polling như các phương tiện vận chuyển có sẵn. Trên máy này tôi cũng chạy một phiên bản của Redis (2.2)

Tôi lưu trữ dữ liệu liên tục (như người dùng và điểm số) trên máy chủ thứ hai trên mongodb (3.6) với 4gigs RAM và 2 lõi.

Ứng dụng này đã được sản xuất từ ​​vài tháng nay (nó đã chạy trên một hộp duy nhất cho đến vài tuần trước) và nó được sử dụng bởi khoảng 18 nghìn người dùng mỗi ngày. Nó luôn luôn hoạt động rất tốt ngoài một vấn đề chính: suy giảm hiệu suất. Khi sử dụng, số lượng cpu được sử dụng bởi mỗi quy trình sẽ tăng lên cho đến khi nó thống kê công nhân (sẽ không phục vụ các yêu cầu nữa). Tôi đã tạm thời giải quyết nó kiểm tra cpu được sử dụng bởi mỗi công nhân mỗi phút và khởi động lại nếu nó đạt 98%. Vì vậy, vấn đề ở đây chủ yếu là cpu chứ không phải RAM. RAM không còn là vấn đề nữa kể từ khi tôi cập nhật lên socket.io 0.9.14 (phiên bản trước đó đã bị rò rỉ bộ nhớ), vì vậy tôi nghi ngờ đây là vấn đề rò rỉ bộ nhớ, đặc biệt là vì hiện tại cpu phát triển khá nhanh ( Tôi phải khởi động lại mỗi công nhân khoảng 10-12 lần một ngày!). Thành thật mà nói, RAM được sử dụng cũng tăng lên. nhưng rất chậm, 1 gig mỗi 2-3 ngày sử dụng, và điều kỳ lạ là nó không được phát hành ngay cả khi tôi khởi động lại toàn bộ ứng dụng. Nó chỉ được phát hành nếu tôi khởi động lại máy chủ! điều này tôi thực sự không thể hiểu ...

Bây giờ tôi đã phát hiện ra một cách đáng kinh ngạc, vì vậy cuối cùng tôi cũng có thể thấy những gì đang xảy ra trên máy chủ sản xuất của mình và tôi đang thu thập dữ liệu sau vài ngày. Nếu bất cứ ai muốn xem các biểu đồ tôi có thể cung cấp cho bạn quyền truy cập, nhưng về cơ bản tôi có thể thấy rằng tôi có từ 80 đến 200 kết nối đồng thời! Tôi đã mong đợi node.js xử lý hàng ngàn chứ không phải hàng trăm yêu cầu. Ngoài ra, thời gian phản hồi trung bình cho lưu lượng truy cập http nổi trong khoảng 500 đến 1500 mili giây mà tôi nghĩ là rất nhiều. Ngoài ra, trong thời điểm này với 1300 người dùng trực tuyến, đây là đầu ra của "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

điều đó cho thấy tôi đã có rất nhiều kết nối kín trong thời gian chờ. Tôi đã tăng các tệp mở tối đa lên 999999, đây là đầu ra của 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

Vì vậy, tôi nghĩ rằng vấn đề có thể là do lưu lượng truy cập http vì một số lý do bão hòa các cổng / ổ cắm có sẵn (?), Nhưng có một điều không có ý nghĩa với tôi: tại sao khi tôi khởi động lại công nhân và tất cả các máy khách kết nối lại trong vài giây, tải trên cpu của công nhân giảm xuống 1% và có khả năng phục vụ các yêu cầu đúng cách cho đến khi nó bão hòa sau khoảng 1 giờ (lúc cao điểm)?

Tôi chủ yếu là một lập trình viên javascript, không phải là quản trị viên hệ thống, vì vậy tôi không biết mình nên tải bao nhiêu để xử lý với các máy chủ của mình, nhưng chắc chắn nó không hoạt động như bình thường. Ứng dụng này hoạt động ổn định và vấn đề cuối cùng này đang ngăn cản tôi gửi các phiên bản di động của ứng dụng đã sẵn sàng, vì rõ ràng chúng sẽ mang lại nhiều tải hơn và cuối cùng làm hỏng toàn bộ!

Hy vọng rằng có một điều rõ ràng là tôi đang làm sai, và ai đó sẽ giúp phát hiện ra điều đó ... cứ thoải mái hỏi tôi để biết thêm thông tin, và tôi xin lỗi vì độ dài của câu hỏi nhưng tôi tin là cần thiết ... cảm ơn trước!


Có cách nào để có được một cái gì đó như kết xuất luồng từ node.js không? Có lẽ có một số chủ đề trong một vòng lặp vô hạn. Ngoài ra, những gì thực sự sử dụng cpu? Bạn thấy gì topkhi sử dụng cpu gần 100%?
rvs

cpu được sử dụng hoàn toàn bởi nodejs, khi tôi chạy top tôi thấy các quá trình nút lấy tất cả các cpu. Không chắc chắn làm thế nào tôi có thể xuất kết xuất luồng từ nút thành thật ...
Franjanko

một điều nữa phải nói là phần lớn thời gian của CPU dường như đi vào hệ thống chứ không phải thời gian của người dùng
Franjanko

Có ai ít nhất biết tôi có thể xử lý bao nhiêu kết nối đồng thời với các máy chủ tôi đã đặt không? tại thời điểm này tôi hỗ trợ tối đa 200 kết nối đồng thời. Điều này sẽ giúp tôi ước tính khoảng cách từ một cấu hình tối ưu ... cảm ơn.
Franjanko

Câu trả lời:


10

Sau vài ngày thử nghiệm và sai sót nghiêm trọng, tôi rất vui khi có thể nói rằng tôi đã hiểu nút thắt ở đâu và tôi sẽ đăng nó ở đây để những người khác có thể hưởng lợi từ những phát hiện của tôi.

Vấn đề nằm ở các kết nối pub / sub mà tôi đang sử dụng với socket.io và đặc biệt là trong RedisStore được sử dụng bởi socket.io để xử lý giao tiếp giữa các quá trình của socket.

Sau khi nhận ra rằng tôi có thể dễ dàng thực hiện phiên bản pub / sub của riêng mình bằng redis, tôi quyết định dùng thử và xóa redisStore khỏi socket.io, để lại nó với kho lưu trữ bộ nhớ mặc định (tôi không cần phải phát tất cả các máy khách được kết nối nhưng chỉ giữa 2 người dùng khác nhau được kết nối có thể trên các quy trình khác nhau)

Ban đầu tôi chỉ khai báo 2 kết nối redis toàn cầu x xử lý pub / sub trên mỗi máy khách được kết nối và ứng dụng đang sử dụng ít nguồn hơn nhưng tôi vẫn bị ảnh hưởng bởi tốc độ tăng trưởng sử dụng CPU liên tục, do đó không thay đổi nhiều. Nhưng sau đó tôi quyết định thử tạo 2 kết nối mới để làm lại cho mỗi khách hàng để xử lý pub / sub của họ chỉ trong các phiên của họ, sau đó đóng các kết nối sau khi người dùng ngắt kết nối. Sau một ngày sử dụng trong sản xuất, cpu vẫn ở mức 0-5% ... lô tô! không có quá trình khởi động lại, không có lỗi, với hiệu suất mà tôi mong đợi sẽ có. Bây giờ tôi có thể nói rằng node.js đá và rất vui khi đã chọn nó để xây dựng ứng dụng này.

May mắn thay, redis đã được thiết kế để xử lý nhiều kết nối đồng thời (khác với mongo) và theo mặc định, nó được đặt ở mức 10k, để lại khoảng 5k cho người dùng đồng thời, trong một trường hợp redis duy nhất, đủ cho tôi, nhưng tôi Tôi đã đọc rằng nó có thể được đẩy lên tới 64k kết nối đồng thời, vì vậy kiến ​​trúc này phải đủ vững chắc.

Tại thời điểm này, tôi đã nghĩ đến việc triển khai một số nhóm kết nối để làm lại, để tối ưu hóa nó thêm một chút, nhưng không chắc liệu điều đó sẽ không gây ra sự kiện pub / sub để xây dựng các kết nối, trừ khi mỗi sự kiện đó bị phá hủy và tái tạo mỗi lần, để làm sạch chúng.

Dù sao, cảm ơn câu trả lời của bạn, và tôi sẽ tò mò muốn biết bạn nghĩ gì, và nếu bạn có bất kỳ đề nghị nào khác.

Chúc mừng.


2
Tôi đang gặp vấn đề tương tự trong ứng dụng sản xuất của mình, cũng mới đối với vai trò quản trị viên máy chủ. Tôi làm theo những gì bạn đã làm trong khái niệm, nhưng tôi có một số câu hỏi về cách thực hiện - có lẽ bạn có thể cung cấp một liên kết đến một số tài nguyên trong câu trả lời được chấp nhận của bạn? Hoặc đơn giản là cung cấp thêm thông tin? Cụ thể về "Nhưng sau đó tôi đã quyết định thử tạo 2 kết nối mới để làm lại cho mỗi khách hàng để xử lý pub / sub của họ chỉ trong các phiên của họ, sau đó đóng các kết nối sau khi người dùng ngắt kết nối."
toblerpwn

2

Bạn có một số mã nguồn để đổ? Nó có thể là kết nối đến cơ sở dữ liệu không đóng? Quá trình chờ kết nối HTTP không bao giờ đóng.

Bạn có thể đăng một số nhật ký?

Làm một ps -ef và đảm bảo không có gì vẫn đang chạy. Tôi đã thấy các quy trình web để lại những thây ma sẽ không chết cho đến khi bạn giết -9. Đôi khi tắt máy không hoạt động hoặc không hoạt động đầy đủ và các luồng hoặc quy trình đó sẽ giữ RAM và đôi khi CPU.

Nó có thể là một vòng lặp vô hạn ở đâu đó trong mã hoặc một quá trình bị lỗi giữ kết nối db.

Những mô-đun NPM đang sử dụng? Có phải tất cả đều là mới nhất?

Bạn đang bắt ngoại lệ? Xem: http://geoff.greer.fm/2012/06/10/nodejs-deals-with-errors/ Xem: /programming/10122245/capture-node-js-crash-reason

Mẹo chung:

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

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

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

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

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

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


1

Không phải là một câu trả lời, vì câu hỏi của bạn là một câu chuyện hơn là một câu hỏi chỉ ra một câu trả lời.

Chỉ cần nói rằng tôi đã xây dựng thành công máy chủ node.js với socket.io xử lý hơn 1 triệu kết nối liên tục với tải trọng tin nhắn trung bình là 700 Byte.

Thẻ giao diện mạng ở tốc độ 1Gbps lúc đầu đã bão hòa và tôi đã thấy rất nhiều I / O chờ đợi từ việc xuất bản các sự kiện cho tất cả các khách hàng.

Loại bỏ nginx khỏi vai trò proxy cũng đã trả lại bộ nhớ quý giá, bởi vì để đạt được một triệu kết nối liên tục chỉ với MỘT máy chủ, là một công việc khó khăn trong việc điều chỉnh cấu hình, ứng dụng và điều chỉnh các tham số HĐH. Hãy nhớ rằng chỉ có thể thực hiện được với rất nhiều RAM (khoảng 1 triệu kết nối websockets ăn khoảng 16GB RAM, với node.js, tôi nghĩ rằng sử dụng sock.js sẽ lý tưởng cho việc tiêu thụ bộ nhớ thấp, nhưng hiện tại, socket.io tiêu thụ nhiều như vậy).

Liên kết này là điểm khởi đầu của tôi để đạt được khối lượng kết nối với nút đó. Bên cạnh đó là một ứng dụng Erlang, tất cả các điều chỉnh hệ điều hành là không rõ ràng về ứng dụng và nên được sử dụng bởi bất kỳ ai nhắm đến nhiều kết nối liên tục (websockets hoặc bỏ phiếu dài).

HTH

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.