NodeJs の高メモリ使用量のトラブルシューティング実戦記録

NodeJs の高メモリ使用量のトラブルシューティング実戦記録

序文

これは、オンライン コンテナーの拡張によって発生した調査です。最終的には、実際の OOM が原因ではないことが判明しましたが、調査のプロセスを要約して記録しました。プロセス全体は、段階的に手がかりを探し、段階的に結果を検証する、事件解決のようでした。

私個人としては、これを行うことの意義と必要性にはいくつかの側面があると考えています。

  1. プログラマーの視点から:コードの完璧さを追求し、問題を放置せず、ビジネスの安定性を確保する
  2. 資源の観点から:それは無意味な資源消費を減らすことです
  3. 企業の観点から:サーバーコストを削減し、企業のコストを節約する

環境: Tencent Taf プラットフォーム上で実行される NodeJs サービス。

問題の原因

最初は、タイミング関数が起動された後、オンラインコンテナが自動的に拡張されたためでした。NodeJsサービス自体はいくつかのインターフェースクエリとsocket.io関数のみであり、大きなトラフィックや高い同時実行性はないため、実際にはサービスは8つのコンテナに拡張する必要があります(1つのコンテナに2Gのメモリが割り当てられます)。これを考えると、メモリリークを疑いました。同時に、ログにメモリ不足が表示されることもあります。

拡大の理由

運用保守担当者に問い合わせたところ、メモリ使用量が限界値に達したことが原因であることが判明しました。

負荷条件

まず、メモリ使用量の増加が過度のサービス負荷によって引き起こされている可能性を排除する必要があります。これは通常のビジネス現象である可能性があります。

監視を通じて、トラフィックと CPU 使用率はそれほど高くなく、非常に低いとさえ言えることがわかりました。したがって、このような高いメモリ使用量は異常な現象です。

これはメモリの問題によって発生し、徐々に継続的に増加する現象があるため、メモリ リークと考えられます。一般的な方法は、「ヒープ スナップショット」または heapsnapshot ファイルを印刷することです。

コンテナを入力します:

ノード名

NodeJsプロジェクトフォルダに入る

/usr/local/app/taf/サービス名/bin/src

スナップショットを生成します。

定数ヒープダンプ = require('ヒープダンプ');
heapdump.writeSnapshot('./' + new Date().getTime() + '.heapsnapshot', 関数(err, ファイル名) {
    console.log('ダンプが書き込まれました', ファイル名);
});

lrzsz コマンドを使用してコンテナ内のファイルを直接転送すると速度が遅いため、scp コマンドを使用して静的リソース サーバーに転送し、ブラウザーからダウンロードする必要があります。

scp 1620374489828.heapsnapshot ユーザー名@ip:/data/static/snapshot

ヒープスナップショットを比較する

サービスを起動して一定時間実行すると 2 つのスナップショットが生成され、比較すると Websocket Socket などのキーワードのみが大まかに確認できます。

さらに拡大しても、それが特定の機能によって引き起こされたかどうかはわかりません。

スナップショットには手がかりがないようです。プロジェクト全体の業務コードはそれほど大きくないので、1行ずつレビューしましたが、OOMの原因となるような異常な記述はないようです。実際、業務コードが小さければ問題ありません。大規模なプロジェクトの場合、このアプローチは費用対効果が低く、直接コードレビューに行くのではなく、何らかの診断方法を使用して確認する必要があります。

スナップショットを数回印刷し、数回読み取った後でも、「websocket」という単語が表示されたので、問題はソケット リンクが解放されていないことが原因ではないかと考えました。

Google で WebSocket メモリ リークを検索したところ、実際に存在することがわかりました。解決策は、perMessageDeflate を追加して圧縮を無効にすることです。現在、socket-io の下位バージョンがデフォルトで有効になっているため、それを追加してしばらくメモリ使用量を観察しましたが、明らかな低下はありませんでした。リリース後もメモリ使用量は非常に高かった。

設定構文:

'socket.io' を要求します。サーバーを listen します。{perMessageDeflate: false};

クライアントから送信されたリクエストには次のフィールドが含まれます。

まず、このパラメータはデータを圧縮するために使用されます。クライアント側ではデフォルトで有効になっており、サーバー側では無効になっています。何らかの理由で、これを有効にするとメモリとパフォーマンスが消費されます。有効にするかどうかを決定する前に、これを考慮することが公式の推奨事項です。ただし、バージョン ^2.3.0 などの socket-io の下位バージョンは有効になっています (これはバグのようで、以降のバージョンではデフォルトで閉じるように変更されています)。

この拡張機能は、サーバーではデフォルトで無効になっており、クライアントではデフォルトで有効になっています。これにより、パフォーマンスとメモリ消費の面で大きなオーバーヘッドが追加されるため、本当に必要な場合にのみ有効にすることをお勧めします。

https://github.com/socketio/socket.io/issues/3477#issuecomment-610265035

電源を入れた後もメモリ使用量は高いままです。

コンソールログ

もう 1 つの現象は、既存の Node サービスがログを出力することです。インターネットで NodeJs のメモリ リークに関する記事をいくつか調べたところ、コンソール ログ出力によってリークが発生していることがわかりました。そこで、コンソールをコメント アウトして、メモリ使用量の監視を続けました。結果は、依然としてメモリ使用量が高くなっていました。

手がかりはここで終わっているようで、手がかりは何もありません。

ログ

翌日、偶然ログ ファイルを見ました。サービスの開始時にいくつかの起動ログが印刷されるため、出力が重複していることがわかりました。

これは、システムが繰り返し実行されていることを示しています。この仮説を確認するには、top コマンドを使用して結果を表示します。

TOPコマンド

同時に、具体的なメモリ使用量も確認したいと思います。ワーカープロセスが非常に多いことがわかりました。現在の業務の実際の使用状況からすると、2〜4 個で十分ではないでしょうか。なぜこれほど多くの子プロセスを開く必要があるのでしょうか。

PID ユーザー PR NI 仮想リソース SHR S %CPU %MEM 時間+ コマンド                                                                                                                       
 90359 ユーザー名 20 0 736m 38m 14m S 0.0 0.0 0:07.30 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90346 ユーザー名 20 0 864m 38m 14m S 0.3 0.0 0:07.08 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90381 ユーザー名 20 0 730m 38m 14m S 0.3 0.0 0:08.75 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90366 ユーザー名 20 0 804m 37m 14m S 0.0 0.0 0:06.94 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90618 ユーザー名 20 0 730m 37m 14m S 0.0 0.0 0:08.42 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90326 ユーザー名 20 0 736m 37m 14m S 0.0 0.0 0:08.46 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90542 ユーザー名 20 0 736m 37m 14m S 0.0 0.0 0:08.85 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90332 ユーザー名 20 0 799m 37m 14m S 0.0 0.0 0:07.32 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90580 ユーザー名 20 0 732m 37m 14m S 0.3 0.0 0:08.94 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90602 ユーザー名 20 0 731m 37m 14m S 0.3 0.0 0:08.33 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90587 ユーザー名 20 0 735m 37m 14m S 0.0 0.0 0:08.83 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90568 ユーザー名 20 0 731m 37m 14m S 0.0 0.0 0:08.83 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90544 ユーザー名 20 0 729m 37m 14m S 0.0 0.0 0:09.07 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90556 ユーザー名 20 0 729m 37m 14m S 0.0 0.0 0:08.82 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90431 ユーザー名 20 0 735m 37m 14m S 0.0 0.0 0:08.29 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90486 ユーザー名 20 0 729m 37m 14m S 0.0 0.0 0:09.06 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90516 ユーザー名 20 0 735m 37m 14m S 0.0 0.0 0:08.95 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90465 ユーザー名 20 0 729m 37m 14m S 0.0 0.0 0:09.06 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90527 ユーザー名 20 0 735m 37m 14m S 0.0 0.0 0:08.46 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90487 ユーザー名 20 0 732m 37m 14m S 0.3 0.0 0:08.48 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90371 ユーザー名 20 0 731m 37m 14m S 0.3 0.0 0:08.75 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90423 ユーザー名 20 0 729m 36m 14m S 0.3 0.0 0:08.09 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90402 ユーザー名 20 0 729m 36m 14m S 0.3 0.0 0:08.96 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90500 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:08.70 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90353 ユーザー名 20 0 729m 36m 14m S 0.3 0.0 0:08.95 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90636 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:08.84 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90425 ユーザー名 20 0 732m 36m 14m S 0.0 0.0 0:08.78 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90506 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:08.84 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90589 ユーザー名 20 0 729m 36m 14m S 0.3 0.0 0:09.05 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90595 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:09.03 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90450 ユーザー名 20 0 729m 36m 14m S 0.3 0.0 0:08.97 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90531 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:08.99 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90509 ユーザー名 20 0 735m 36m 14m S 0.0 0.0 0:08.67 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90612 ユーザー名 20 0 730m 36m 14m S 0.3 0.0 0:08.84 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90479 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:08.58 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90609 ユーザー名 20 0 731m 36m 14m S 0.3 0.0 0:09.23 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90404 ユーザー名 20 0 734m 36m 14m S 0.3 0.0 0:08.78 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90395 ユーザー名 20 0 736m 36m 14m S 0.0 0.0 0:08.57 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90444 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:09.04 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90438 ユーザー名 20 0 729m 36m 14m S 0.3 0.0 0:07.78 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90340 ユーザー名 20 0 736m 36m 14m S 0.3 0.0 0:07.37 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90333 ユーザー名 20 0 729m 36m 14m S 0.0 0.0 0:07.60 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90563 ユーザー名 20 0 735m 36m 14m S 0.3 0.0 0:08.93 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90565 ユーザー名 20 0 734m 36m 14m S 0.3 0.0 0:08.77 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90457 ユーザー名 20 0 735m 36m 14m S 0.0 0.0 0:08.31 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90387 ユーザー名 20 0 740m 36m 14m S 0.0 0.0 0:07.59 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90573 ユーザー名 20 0 728m 35m 14m S 0.0 0.0 0:09.06 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90472 ユーザー名 20 0 728m 35m 14m S 0.0 0.0 0:08.94 /usr/local/app/service_name/bin/src/index.js: ワーカープロセス                                                      
 90313 ユーザー名 20 0 588m 27m 13m S 0.0 0.0 0:00.46 /usr/local/app/service_name/bin/src/index.js: マスタープロセス

%MEM 列の値はコンテナ内の特定のメモリ使用量を示しておらず、常に 0.0 と表示されるため、VIRT、RES、SHR の値を確認する必要があります。これらの意味については、https://www.orchome.com/298 を参照してください。

RES の方が重要です。RES は、物理メモリ空間にマップされているプロセス仮想メモリ空​​間の部分のサイズを指します。したがって、ワーカー プロセスは 35 ~ 38M のメモリ サイズを占有していることがわかります。ワーカー プロセスは 48 個、マスター プロセスは 1 個あります。

48 個のワーカー プロセスはどのようにして発生したのでしょうか? CPU の論理数を照会すると、実際には 48 個あることがわかります。

# コアの合計数 = 物理 CPU の数 x 物理 CPU あたりのコアの数 # 論理 CPU の合計数 = 物理 CPU の数 x 物理 CPU あたりのコアの数 x ハイパースレッドの数 # 物理 CPU の数を表示します cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l

# 各物理CPUのコア数(コア数)を表示します
cat /proc/cpuinfo| grep "CPU コア"| uniq

# 論理 CPU の数を表示します cat /proc/cpuinfo | grep "processor" | wc -l

プロセス数を制御する

私は Taf プラットフォームにあまり詳しくないので、NodeJS を taf で実行するには、対応するパッケージ @tars/node-agent が必要であることを知りました。公式 Web サイトの使用法ドキュメントを確認しました: https://tarscloud.github.io/TarsDocs/dev/tars.js/tars-node-agent.html

-i設定があり、これはインスタンスを表します

-i, --インスタンス

node-agent は、Node.js ネイティブ Cluster モジュールを使用して負荷分散を実現します。

ここで、ノードエージェントによって開始されるサブプロセス (ビジネス プロセス) の数を設定できます。

設定されていない場合(または auto または 0 に設定されている場合)、開始される子プロセスの数は CPU 物理コアの数と同じになります。

max に設定すると、開始される子プロセスの数は CPU (すべてのコア) の数と等しくなります。

tarsnode によってノードエージェントが起動されると、TARS 構成ファイルの tars.application.client.asyncthread 構成セクションが自動的に読み取られます。

また、TARS プラットフォーム -> サービスの編集 -> 非同期スレッドの数から調整することもできます。

https://tarscloud.github.io/TarsDocs/dev/tars.js/tars-node-agent.html
このパッケージを使用して、Taf 上で NodeJs サービスを起動し、負荷分散機能を有効にします。子プロセス (業務プロセス) の具体的な数は設定されていないため、デフォルトでは CPU 物理コアの数が使用されます。CPU が 2 個あるため、2 倍になり、合計 48 個のワーカー プロセスが生成されます。ワーカー プロセスごとにメモリを占有するため、メモリ使用量は高いままです。

「プライベート テンプレート」で設定を変更できます。

次に、サービスを再起動してメモリ使用量を確認します。

ワーカー プロセスの数がメモリ使用量に影響していることがわかります。元のメモリ使用量の傾向グラフは増加し続けます (これが、最初にメモリ リークを疑った理由です)。この問題は、ワーカー プロセスの数を減らした後には現れませんでした。今は無視して、後で引き続き観察します。

重複したコンソールとワーカー プロセスの関係を確認するために、2 つのワーカー プロセスを開始してログを確認したところ、ログが実際に 2 回印刷されていることが分かりました。

要約する

この問題を見直してみましょう:

なぜ間に合わずに発見されなかったのでしょうか?

これは、バックエンド サービスの一部の機能にあまり敏感ではないフロントエンド開発者の役割に関連している可能性があります。私はそれに全く注意を払っていません、あるいはそれを知らないか理解していません。

事前に回避することはできますか?

Node サービスのメモリが増加傾向にあることを通知する同様のアラーム メカニズムが存在する可能性があります。私はまだ Taf プラットフォームの機能に精通していないため、後で調べてみます。

NodeJs での高メモリ使用量のトラブルシューティングに関するこの記事はこれで終わりです。NodeJs での高メモリ使用量の詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript のメモリ空間、割り当て、深いコピーと浅いコピーの詳細な説明
  • JavaScript のメモリリークを理解するための記事
  • Node.js でメモリ効率の高いアプリケーションを作成する方法
  • JavaScript のガベージコレクションメカニズムとメモリ管理
  • 一般的な JS メモリ リークとその解決策の分析
  • JavaScript メモリ モデルの例の詳細な説明
  • JS によるメモリリークの例をいくつか分析する
  • JavaScriptのスタックメモリとヒープメモリの詳しい説明
  • JavaScript のメモリリークに対処する方法
  • JSメモリ空間の詳細な説明

<<:  Linux でタスク用のカスタム システム トレイ インジケーターを作成する

>>:  MySql 8.0.11 のインストールと設定のチュートリアル

推薦する

MySQL 5.0.96 for Windows x86 32 ビット グリーン簡易版インストール チュートリアル

MySQL 5.0 は、いくつかの「高度な機能」があるため定番となっています。これは、Windows...

基礎知識: ウェブサイトのアドレスの前の http はどういう意味ですか?

HTTPとは何ですか?ウェブサイトを閲覧したいときは、ブラウザのアドレス バーにウェブサイトのアド...

1 時間で MySQL データベースを学ぶ (Zhang Guo)

目次1. データベースの概要1.1 開発の歴史2. MySQL の紹介2.1. MySQLの概要2....

Linux環境でログファイルを表示するコマンドの詳細な説明

目次序文1. catコマンド: 2. moreコマンド: 3. lessコマンド: 4. headコ...

MySQL 8.0.11 MSI バージョンのインストールと構成のグラフィック チュートリアル

この記事では、MySQL 8.0.11 MSIバージョンのインストールと設定のチュートリアルを参考ま...

Vueのsync修飾子の詳細な説明

目次1. 手順2. 修飾語3. .sync 修飾子4. まとめ1. 手順指示とは命令です。文字通りの...

Linux マルチスレッドにおけるフォークとミューテックス ロック プロセスの例

目次質問: 1. 最初の試み2. 合理的な分析3. 問題解決(1) pthread_join()の使...

バッテリー残量が少なくなったときに Linux を自動シャットダウンする方法

序文最近、私の住居の電力事情が不安定で、突然の停電が頻繁に起こります。ノートパソコンを持っているので...

デザイナーはコーディングを学ぶ必要がありますか?

多くの場合、 Web デザインが完成した後でデザイナーの無知が露呈し、批判されることがあります。彼ら...

Centos7でのSambaサーバー構成(実戦)

サンバの概要Samba は、Linux および UNIX システム上で SMB プロトコルを実装する...

mysql5.7.19 zip 詳細なインストールプロセスと構成

MySQL v5.7.19 正式版(32/64 ビットインストール版および zip 解凍版) 1. ...

初心者のための HTML コーディングガイドライン 30 選

1. HTMLタグは常に閉じる前のページのソース コードでは、次のような記述がよく見られます。 &l...

Linux で環境変数 JAVA_HOME を変更/設定する方法について簡単に説明します。

1. 永久的な変更、すべてのユーザーに有効# vi /etc/プロファイル//キーボードの[Shi...

Vueは小さな天気予報アプリケーションを実装します

これは私が Vue フレームワークを独学していたときに真似したウェブサイトです。いくつかの都市の天気...