node.js, mongodb, redis, on ubuntu 生產性能下降,RAM 空閒,CPU 100%
正如問題標題所暗示的那樣,我很難弄清楚我的應用程序可以改進什麼(或在作業系統、ubuntu 中進行調整)以達到可接受的性能。但首先我將解釋架構:
前端伺服器是一台執行 Ubuntu 12.04 的 8 核機器,具有 8 gigs RAM。該應用程序完全用 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) 實例
我將持久數據(如使用者和分數)儲存在具有 4gigs RAM 和 2 個核心的 mongodb(3.6)上的第二台伺服器上。
該應用程序幾個月前就投入生產(直到幾週前它一直在一個盒子上執行),每天有大約 18,000 名使用者使用它。除了一個主要問題:性能下降之外,它一直執行良好。隨著使用,每個程序使用的 cpu 數量會增長,直到它使 worker 成熟(它將不再為請求提供服務)。我暫時解決了它每分鐘檢查每個工作人員使用的 cpu,如果達到 98% 則重新啟動它。所以這裡的問題主要是cpu,而不是RAM。RAM 不再是問題,因為我已經更新到 socket.io 0.9.14(早期版本正在洩漏記憶體)所以我懷疑它是一個記憶體洩漏問題,特別是因為現在它是 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
這表明我在 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流量上,由於某些原因使可用埠/套接字(?)飽和,但有一件事對我來說沒有意義:為什麼當我重新啟動工作人員並且所有客戶端在幾秒鐘內重新連接時,工作人員的 CPU 負載下降到 1% 並且能夠正確處理請求,直到大約 1 小時後(高峰時間)飽和?
我主要是 javascript 程序員,而不是系統管理員,所以我不知道我的伺服器應該處理多少負載,但它肯定沒有按應有的方式執行。否則應用程序是穩定的,最後一個問題是阻止我發布準備好的應用程序的移動版本,因為顯然它們會帶來更多負載並最終導致整個事情崩潰!
希望有一些明顯的事情我做錯了,有人會幫助發現它……隨時向我詢問更多資訊,我很抱歉問題的長度,但我相信這是必要的……提前致謝!
經過幾天的激烈試驗和錯誤,我很高興能夠說我已經理解了瓶頸所在,我會在這裡發布它,以便其他人可以從我的發現中受益。
問題在於我與 socket.io 一起使用的 pub/sub 連接,特別是在 socket.io 用於處理套接字實例的程序間通信的 RedisStore 中。
在意識到我可以使用 redis 輕鬆實現我自己的 pub/sub 版本後,我決定試一試,並從 socket.io 中刪除了 redisStore,將其保留為預設記憶體儲存(我不需要廣播到所有連接的客戶端,但僅在可能在不同程序上連接的 2 個不同使用者之間)
最初,我只聲明了 2 個全域 redis 連接 x 程序來處理每個連接的客戶端上的 pub/sub,並且應用程序使用的資源更少,但我仍然受到 CPU 使用率持續增長的影響,所以沒有太大變化。但後來我決定嘗試為每個客戶端創建 2 個到 redis 的新連接,以便僅在他們的會話上處理他們的發布/訂閱,然後在使用者斷開連接後關閉連接。然後在生產中使用一天后,cpu 仍然在 0-5% ……賓果!沒有程序重新啟動,沒有錯誤,具有我期望的性能。現在我可以說 node.js 非常棒,並且很高興選擇它來建構這個應用程序。
幸運的是,redis 被設計為處理許多並發連接(由 mongo 不同),預設情況下它設置為 10k,在單個 redis 實例上為大約 5k 並髮使用者留出了空間,這對我來說已經足夠了,但我已經讀到它可以被推送到 64k 並發連接,所以我相信這個架構應該足夠穩固。
此時我正在考慮為redis實現某種連接池,以進一步優化它,但不確定這是否不會再次導致發布/訂閱事件在連接上建立,除非它們中的每一個每次都被破壞並重新創建,以清理它們。
無論如何,感謝您的回答,我很想知道您的想法,以及您是否有其他建議。
乾杯。