web-dev-qa-db-ja.com

node.js、mongodb、redis、本番環境でのUbuntuのパフォーマンス低下、RAMは無料、CPU 100%

質問のタイトルが示唆しているように、許容できるパフォーマンスを達成するために、アプリケーションで何を改善できるか(またはos、ubuntuで調整できるか)を理解するのに苦労しています。ただし、最初にアーキテクチャについて説明します。

フロントエンドサーバーは、8ギグの8コアマシンですRAM Ubuntu 12.04を実行しています。アプリケーションはすべてJavaScriptで記述され、node.js v 0.8.22で実行されます(一部のモジュールでは新しいバージョンのノードで不平を言う)私はnginx 1.4を使用して、ポート80および443からノードクラスターAPIを使用して管理および開始される8ノードワーカーにhttpトラフィックをプロキシします。最新バージョンのsocket.io 0.9.14を使用して、使用可能なトランスポートとしてwebsocketとxhr-pollingのみを有効にしたwebsocket接続このマシンでは、Redis(2.2)のインスタンスも実行しています

4gigs RAMと2つのコアを持つmongodb(3.6)の2番目のサーバーに永続的なデータ(ユーザーやスコアなど)を保存します。

アプリは数か月前から稼働しており(数週間前まで単一のボックスで実行されていました)、1日あたり約18,000人のユーザーが使用しています。これは常に、パフォーマンスの低下という1つの主要な問題とは別に、非常にうまく機能しています。使用すると、各プロセスで使用されるCPUの量は、ワーカーを確定するまで増加します(ワーカーはリクエストを処理しなくなります)。私は一時的に各ワーカーが使用しているCPUを毎分チェックし、98%に達したら再起動することで解決しました。したがって、ここでの問題は主にCPUであり、RAMではありません。 RAMは、socket.io 0.9.14に更新してから以前のバージョンでは問題になりません(以前のバージョンではメモリリークが発生していました)。メモリリークの問題ではないかと思います。特に、かなり急速に成長するのはcpuです(各ワーカーを1日に10〜12回再起動する必要があります!)RAM使用中も成長しますが、正直に言えば非常にゆっくりです1使用の2〜3日ごとにギグしますが、奇妙なことに、アプリケーション全体を完全に再起動してもリリースされません。サーバーを再起動した場合にのみリリースされます!これは本当に理解できません...

nodefly を発見しました。すごいので、本番サーバーで何が起こっているのかを最終的に確認でき、2、3日以来データを収集しています。誰かがグラフを表示したい場合は、アクセスを許可できますが、基本的には、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プログラマーであり、システム管理者ではないので、サーバーでどの程度の負荷を処理する必要があるかわかりませんが、期待どおりに動作していません。それ以外の場合はアプリケーションは安定しており、この最後の問題により、準備が整ったモバイルバージョンのアプリを出荷できません。明らかに、負荷が高くなり、最終的にすべてがクラッシュするためです。

うまくいけば、私が間違っていることは明らかであり、誰かがそれを見つけるのを手伝ってくれます...詳細についてお気軽に尋ねてください。質問の長さで申し訳ありませんが、必要だったと思います...前もって感謝します!

11
Franjanko

数日間の激しい試行錯誤の末、ボトルネックがどこにあるかを理解できたと言えると嬉しく思います。他の人が私の発見から利益を得られるように、ここに投稿します。

問題は、socket.ioで使用していたpub/sub接続にあります。特に、socket.ioがソケットインスタンスのプロセス間通信を処理するために使用するRedisStoreにあります。

Redisを使用して自分のバージョンのpub/subを簡単に実装できることを認識した後、試してみることにし、redisStoreをsocket.ioから削除して、デフォルトのメモリストアのままにしました(ブロードキャストする必要はありません)接続されているすべてのクライアント(ただし、異なるプロセスで接続されている可能性がある2人の異なるユーザー間のみ)

最初に、接続されたすべてのクライアントでpub/subを処理するための2つのグローバルredis接続xプロセスのみを宣言しました。アプリケーションはリソースをあまり使用していませんでしたが、CPU使用率の増加による影響を受けていたため、ほとんど変更されていませんでした。しかし、その後、クライアントごとに2つの新しい接続をredisに作成して、セッションでのみpub/subを処理し、ユーザーが切断したら接続を閉じることにしました。その後、本番環境で1日使用した後も、CPUは0〜5%でした...ビンゴ!プロセスの再起動やバグはなく、期待していたパフォーマンスが得られました。これで、node.jsはすばらしいと言えるので、このアプリをビルドするためにnode.jsを選択できてうれしいです。

幸い、redisは多数の同時接続(mongoによって異なる)を処理するように設計されており、デフォルトでは10kに設定されているため、1つのredisインスタンスで約5kの同時ユーザーのためのスペースが残されていますが、今のところ十分ですが、最大64kの同時接続をプッシュできることを読んだので、このアーキテクチャは十分に堅固なはずです。

この時点で、Redisに何らかの接続プールを実装してもう少し最適化することを考えていましたが、それが原因でpub/subイベントが接続で再び発生しないかどうかはわかりませんそれらをきれいにするために毎回破壊され、再作成されます。

とにかく、あなたの答えをありがとう、そして私はあなたがどう思うか、他に何か提案があるかどうか知りたいと思っています。

乾杯。

10
Franjanko

ダンプするソースコードはありますか?データベースへの接続が閉じていない可能性がありますか?決して閉じないHTTP接続を待機しているプロセス。

ログを投稿できますか?

Ps -efを実行して、まだ何も実行されていないことを確認します。私は、あなたがkill -9を行うまで死ぬことのないゾンビをWebプロセスが残すのを見てきました。シャットダウンが機能しない場合や完全に機能しない場合があります。これらのスレッドまたはプロセスはRAMとCPUを保持することがあります。

コードのどこかに無限ループがあるか、db接続を保持しているクラッシュしたプロセスである可能性があります。

どのNPMモジュールが使用していますか?それらはすべて最新ですか?

例外をキャッチしていますか?参照: http://geoff.greer.fm/2012/06/10/nodejs-dealing-with-errors/ 参照: https://stackoverflow.com/questions/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

https://stackoverflow.com/questions/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/

2
Tim Spann

あなたの質問は一回答の指摘質問というよりは物語のようなので、それ自体は答えではありません。

メッセージペイロードの平均が700バイトで、100万を超える永続的な接続を処理するsocket.ioを使用してnode.jsサーバーを正常に構築したことを伝えるだけです。

最初は1Gbpsのネットワークインターフェイスカードが飽和していて、発行イベントからすべてのクライアントへのI/O待機が大量に発生していました。

プロキシの役割からnginxを削除すると、貴重なメモリが返されました。これは、1つのサーバーだけで100万の永続的な接続に到達することは、構成、アプリケーション、およびOSパラメータの調整という難しい仕事です。多くのRAMでのみ実行可能であることを覚えておいてください(100万のWebsocket接続は約16GBのRAMを消費し、node.jsで、sock.jsの使用は低メモリに理想的だと思います消費、しかし今のところ、socket.ioはそれだけを消費します)。

このリンク は、ノードとの接続のボリュームに到達するための出発点でした。 Erlangアプリであることに加えて、すべてのOSチューニングはほとんどアプリケーションに依存せず、多くの永続的な接続(Webソケットまたはロングポーリング)を目的とするすべての人が使用する必要があります。

HTH、

1
Marcel