ホームページ  >  記事  >  ウェブフロントエンド  >  【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有する

【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有する

青灯夜游
青灯夜游転載
2023-01-28 19:25:373018ブラウズ

ノードのメモリ リークをトラブルシューティングするにはどうすればよいですか?次の記事は、ノードのメモリ リークのトラブルシューティング体験をまとめたもので、皆様のお役に立てれば幸いです。

【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有する

Nodejs サーバーサイド開発のシナリオでは、メモリ リーク が間違いなく最も厄介な問題です。 しかし、プロジェクトが開発され反復され続ける限り、メモリ リークの問題は絶対に避けられず、遅かれ早かれ発生するだけです。したがって、効果的な メモリ リーク トラブルシューティング方法を体系的に習得することは、Nodejs エンジニアの最も基本的かつ中心的な能力です。

メモリ リークに対処する際の難しさは、無数の関数や関数の中から、どの行のどの関数や関数がメモリ リークの原因となっているのかを正確に特定する方法です。 残念ながら、現時点ではメモリ リークを簡単に特定できるツールは市場に存在しないため、この問題に初めて遭遇した多くのエンジニアは混乱し、対処方法がわからなくなるでしょう。 ここでは、22 年間に Memory Leak を調査した事例に基づいて、私の対処方法を共有します。

問題の説明

2022 Q4 ある日、研究開発ユーザー グループから、当社の研究開発プラットフォームにアクセスできないことが報告され、多数のユーザーがバックグラウンドで例外が発生しました。タスクは完了していません。 最初の反応は、メモリ リークの可能性があるということです。幸いなことに、サービスはモニタリング (prometheus grafana) に接続されています。grafana モニタリング パネルでは、 10.00 以降、メモリが制御不能になっていることが判明しました。明らかなデータ漏洩が発生しました。 [関連チュートリアルの推奨事項: nodejs ビデオ チュートリアル ]

DX 内存泄漏监控图.png

##手順 :

  • プロセス メモリ: rss (常駐セット サイズ)、プロセスの常駐メモリ サイズ。
  • heapTotal: V8 ヒープの合計サイズ。
  • heapused: 使用される V8 ヒープのサイズ。
  • external: V8 オフヒープ メモリの使用量。

Nodejs のグローバル メソッド process.memoryUsage() を呼び出して、heapTotal と # を含むこれらのデータを取得できます。 ##heapused は V8 ヒープの使用量であり、Node.js 内の JavaScript オブジェクトが保存される場所です。また、external は、C オブジェクトなど、非 V8 ヒープに割り当てられたメモリを表します。 rss は、プロセスの合計メモリ使用量です。一般に、監視データを確認するときは、heapused インジケーターに注目してください。

メモリ リークの種類

メモリ リークは主に 2 つに分類されます。 into:

グローバル リーク
  • ローカル リーク
実際には、グローバル メモリ リークであろうとローカル メモリ リークであろうと、すべてやるべきことは、除外範囲をできる限り狭めることです。

グローバル メモリ リーク

グローバル コンテンツ リークは通常、

ミドルウェア

および コンポーネント で発生します。このタイプのメモリ リークもトラブルシューティングが最も簡単です。 残念ながら、

2022 Q4

で発生したメモリ リークはこのタイプに属さないため、ローカル リークの考え方に従って分析する必要があります。

二分法によるトラブルシューティング

このタイプの他の科学的分析方法については説明しませんが、この場合、二分法を使用するのが最も早いと思います。

プロセス フロー

:

    最初にコードの半分をコメントアウトします (半分に減らします
  • ミドルウェア

    , コンポーネント、または他の一般的なロジックの使用)

  • インターフェイスを選択するか、ストレス テスト用の新しいテスト インターフェイスを作成します
  • Ifメモリリークが発生した場合、リークポイントは現在使用中のコード内にあり、リークがない場合、リークポイントは
  • に表示され、上記のプロセスを約 20 回繰り返します~ 60 分。メモリ リークは確実に特定できます。
の詳細な場所。2020 年に
Nuxt## ベースの SSR アプリケーションを開発していたときのことです。 # では、オンラインにする前のストレス テストでアプリケーション メモリ リークが見つかり、グローバルであることが判明しました。予期せぬリークの後、二分法を使用して問題を特定するのに約 30 分かかりました。

当時のリークの原因は、サーバー側で axios を使用していたためでしたが、その後、
axios を統合し、関連するものをすべて node- に置き換えました。 fetch で解決しました。それ以来、axios PDST に切り替えました。axiosNode サービスで使用することはありません。

永続的なメモリ リークの部分的なトラブルシューティング

ほとんどのメモリ リークはローカル リークです。リーク ポイントは、特定の ミドルウェア

または特定の # に存在する可能性があります。 ##interface

. 特定の 非同期タスク では、このような特性により、トラブルシューティングも困難になります。この場合、分析のために heapdump が実行されます。 <p>ここでは主にこの場合の私のアイデアについて話します。<code>heapdump の詳細な説明は次の段落

Heap Dump に記載します。 : ヒープ ダンプ。以下の部分はすべて heapdump で表されます。heapdump を実行するためのツールやチュートリアルは数多くあります (chrome、vscode、heapdump オープン ソース ライブラリなど)。私が使用するヒープダンプ ライブラリに関するオンライン チュートリアルは多数ありますが、ここでは説明しません。

ローカル メモリ リークのトラブルシューティングには、ある程度のメモリ リークのトラブルシューティングの経験が必要です。問題が発生するたびに、自分自身のテストとして扱ってください。経験を積んでから、メモリのトラブルシューティングを行うことができます。漏れの問題は後で発生しますが、それはますます速くなります。

1. メモリ リークが発生する時間範囲を特定する

これは非常に重要であり、これを知っておくと、調査の範囲が大幅に狭まる可能性があります。
この状況はよく発生します。この反復には 3 つの関数 A、B、C があり、ストレス テスト中またはオンラインになった後にメモリ リークが発生します。その後、直接ロックできるようになり、これら 3 つの新しい関数の間で小さなメモリ リークが発生します。この場合、heapdump を実行するために運用環境に移動する必要はありません。いくつかのツールを使用してローカルでメモリ リーク ポイントを簡単に分析し、特定することができます。

2020 年の Q4 における特殊な状況により、メモリ リークが見つかったとき、メモリ リークが最初に発生した時期を特定することが困難でした。大まかに 1 でロックすることしかできません。数か月以内に。今月もメジャーバージョンアップを行っているのですが、これらの機能やインターフェースを一つ一つチェックしていくと、かなりのコストがかかってしまいます。 したがって、さらに分析するには、より多くのデータを組み合わせる必要があります

2. ヒープダンプ データを収集します

  • 運用開始時ノード追加--expose-gc、このパラメータは gc() メソッドをグローバルに挿入して、GC の手動トリガーを容易にし、より正確な ヒープ スナップショットdata
  • ## を取得します。 # ここでは 2 つのインターフェイスを追加し、独自の排他的アクセス許可を取得しました。
    • GC を手動でトリガーします
    • #印刷ヒープ スナップショット
  • ヒープダンプ
  • #プロジェクト開始後の初めてのスナップショット データの印刷
      メモリが 100M 増加した後: 最初に GC をトリガーし、次に 2 回目にヒープ スナップショット データを印刷します
    • メモリがクリティカルに近づいています GC を再度トリガーして、ヒープ スナップショットを出力します
  • ヒープ スナップショット データを収集するときに特に注意が必要な点がいくつかあります。

ノード サービスは
heapdump
    中に中断されます。この時間は、その時点のサーバー メモリのサイズに応じて約 2 ~ 30 分になります。 。運用環境で
  • heapdump を実行するには、運用および保守と協力して合理的な戦略を策定する必要があります。ここでは、2 つのプライマリ pod とセカンダリ pod を使用します。プライマリ pod が停止すると、ビジネス リクエストは、本番稼働を確保するためにセカンダリ pod にロード バランシングされます。ビジネスの。 (このプロセスは運用保守と緊密に連携したプロセスである必要があります。結局のところ、heapdump もそれらを通じてサーバー内の heap snapshot ファイルを取得する必要があります)
  • クリティカル ポイントでスナップショットを印刷するというのは、漠然とした説明ですが、試したことがある方ならわかると思いますが、メモリ スナップショットを印刷する前にクリティカル ポイントのすぐ近くで待機すると、印刷されません。したがって、この程度に近づくには自分をコントロールする必要があります。
  • 少なくとも 3 回実行してください
  • heapdump(実際には、最も詳細なデータを取得するために 5 回実行しました)

3 . 分析のために監視パネルからのデータを結合します

監視にアクセスするにはアプリケーション サービスが必要です。ここでのアプリケーションは監視に

prometheus grafana を使用します。主にサービスの次の指標を監視します

    #QPS
  • (1 秒あたりのリクエスト訪問数)、リクエストのステータス、およびそのアクセス パス
  • ART
  • (インターフェイスの平均応答時間) とそのアクセス データ
  • NodeJs
  • バージョン
  • アクション ハンドラー
  • (ハンドル)# #イベント ループ ラグ
  • (イベント ラグ)
  • サービス プロセスの再起動数
  • CPU 使用率
  • メモリ使用量:
  • rss
  • ,
  • heapTotalheapusedexternalheapAvailableDetail
heapdump
データのみが含まれません。十分です、

heapdump データは非常に曖昧であり、視覚化ツールのサポートがあっても、問題を正確に特定することは困難です。今回はgrafanaのデータをいくつか組み合わせて見てみました。

私の解析・処理結果

当時のスナップショットデータが紛失してしまったので、ここでシーンをシミュレーションしてみます。

1.

grafana

モニタリング インターフェイスを通じて、メモリが増加していて減少していないことがわかりますが、同時に

の数が減少していることにも気づきました。サービス内のハンドル も急増していますが、落ちることはありません。

2. これは、リークが発生した月の新機能を確認し、メモリ リークは bull メッセージ キュー コンポーネントの使用によって引き起こされているのではないかと疑ったときのことです。まず、関連するアプリケーション コードを分析しましたが、メモリ リークを引き起こすような問題があるとはわかりませんでした。 1.のハンドルリークの問題と合わせると、bullを使用した後に特定のリソースを手動で解放する必要があるようですが、現時点では具体的な理由はわかりません。

3. 次に、5 回分の heapdunmp データを分析し、そのデータを chrome にインポートしました。5 回分のヒープ スナップショットを比較した結果、次のことがわかりました。各キューの作成後、 TCP 、 Socket 、 EventEmitter イベントは解放されません。現時点では、bull の不規則な使用が原因であることはほぼ確実です。 bull では、通常、キューは頻繁に作成されず、キューによって占有されているシステム リソースは自動的に解放されないため、必要に応じて手動で解放する必要があります。

【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有する

#4. コードを調整した後、再度ストレス テストを実行したところ、問題は解決しました。

ヒント: Nodejs の handle は、基礎となるシステム リソース (ファイル、ネットワーク接続など) を指すポインターです。ハンドルを使用すると、Node.js プログラムは、基盤となるシステムと直接対話することなく、これらのリソースにアクセスして操作できるようになります。ハンドルは、Node.js ライブラリまたはモジュールで使用されるハンドルの種類に応じて、整数またはオブジェクトになります。 CommonHandle:

  • #fs.open() 返されるファイル ハンドル
  • net.createServer() 返されるネットワーク サーバー ハンドル
  • dgram.createSocket() 返された UDP ソケット ハンドル
  • child_process.spawn() 返された子プロセス ハンドル
  • #crypto.createHash()
  • 返されたハッシュ ハンドル
  • zlib.createGzip()
  • 返された圧縮ハンドル
#ヒープダンプ分析の概要

通常、ヒープ スナップショット

データを初めて取得するときは、多くの人が混乱します。私も同様です。インターネット上で無数の分析テクニックを読み、それを私自身の実際の実践と組み合わせた後、さらに役立つテクニックをいくつかまとめました。一部の基本的な使用方法のチュートリアルについては、ここでは説明しません。ここでは、

chrome; 概要ビュー

データをインポートした後の画像の見方について主に説明します。このビューでは、通常、まず [保持サイズ] を選択し、次にオブジェクトのサイズと数を観察します。経験豊富なエンジニアは、特定のオブジェクトの数が異常であることをすぐに判断できます。このビューでは、自分で定義したいくつかのオブジェクトを考慮することに加えて、

【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有する

TCP
  • ##ソケット
  • など、メモリ リークが発生しやすい一部のオブジェクトにも注意が必要です。 EventEmitter
  • グローバル
  • 比較ビュー

合格した場合概要ビュー, 問題が特定できない場合は、通常、

Comparison

ビューを使用します。このビューを通じて、2 つのヒープ スナップショット内のオブジェクトの数と、オブジェクトが占有するメモリの変化を比較できます。 この情報を通じて、ヒープ内のオブジェクトの値を判断し、一定の操作 (特定の操作) 後のメモリの変化を判断することができ、これらの値を通じて異常なオブジェクトを見つけることができます。これらのオブジェクトの名前属性または関数によって、メモリ リーク調査の範囲が狭まる可能性があります。 [比較] ビューで 2 つのヒープ スナップショットを選択し、それらを比較します。 2 つのヒープ スナップショット間でどのオブジェクトが追加されたか、2 つのヒープ スナップショット間でどのオブジェクトが削減されたか、どのオブジェクトのサイズが変更されたかを確認できます。

比較

ビューでは、オブジェクト間の関係だけでなく、タイプ、サイズ、参照カウントなどのオブジェクトの詳細も表示できます。この情報により、どのオブジェクトがメモリ リークの原因となっているかを理解できます。

#包含ビュー

【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有する

オブジェクト間の到達可能なすべての参照関係を表示します。各オブジェクトは点として表され、その親オブジェクトに線で接続されます。こうすることで、オブジェクト間の階層関係を確認し、どのオブジェクトがメモリ リークの原因となっているかを把握できます。

#統計ビュー

【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有するこの図は非常に単純なので、詳細については説明しません

メモリ リークのシナリオ

  • グローバル変数: グローバル変数はリサイクルされません
  • キャッシュ: 次のようなメモリ集約型のサードパーティ ライブラリが使用されますlru-cache 保存しすぎるとメモリ不足になります。Nodejs サービスでは lru-cache の代わりに
  • redis
  • を使用することをお勧めします。ハンドル リーク: システム リソースは呼び出し後に解放されません
  • イベント モニタリング
  • 終了
  • 循環参照

概要

  • 問題の種類を初めて特定しやすくするために、サービスにはアクセス監視が必要です

  • メモリ リークがグローバルかローカルかを判断します

  • 二分法を使用して、グローバル メモリ リークのトラブルシューティングを迅速に行い、特定します。

  • ローカル メモリ リーク

    • メモリ リークが発生し、問題を迅速に特定します。 機能
    • ヒープ スナップショット データを少なくとも 3 回収集します。
    • 監視データ、ヒープ スナップショット データ、リーク発生時の新しい関数を組み合わせて、問題を特定します。メモリ リーク ポイント

メモリ リークの問題が発生しても恐れることはありません。メモリ リークの問題のトラブルシューティングでより多くの経験を積んでください。処理の経験が増えるほど、メモリ リークの問題は、すぐに見つけやすくなります。各解決策の後で、レビューと要約を実行し、もう一度振り返りますヒープ スナップショットデータは、関連するエクスペリエンスをより速く蓄積するのに役立ちます

その他

  • ストレス テスト ツール: wrk

ノード関連の知識については、nodejs チュートリアルを参照してください。

以上が【体験談まとめ】Nodeのメモリリークのトラブルシューティング方法は?アイデアを共有するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。