Node.js での高いメモリ使用量を追跡する
この記事では、Node.js での高いメモリ使用量を追跡して修正する私のアプローチを共有します。
コンテンツ
- コンテキスト
-
アプローチ
- コードを理解する
- 問題を個別に再現します
- ステージング サービスからプロファイルをキャプチャする
- 修正を確認します
- 結果
- 結論
コンテクスト
最近、「ライブラリ x のメモリ リーク問題を修正する」というタイトルのチケットを受け取りました。説明には、高メモリ使用量に悩まされ、最終的には OOM (メモリ不足) エラーでクラッシュする十数のサービスを示す Datadog ダッシュボードが含まれており、それらはすべて共通の x ライブラリを持っていました。
私がコードベースを知ったのはつい最近 (2 週間以内) で、それがこのタスクをやりがいのあるものにし、共有する価値のあるものでもありました。
私は 2 つの情報をもとに作業を開始しました:
- すべてのサービスで使用されているライブラリがあり、メモリ使用率が高くなります。これには redis が関係しています (ライブラリの名前には redis が含まれています)。
- 影響を受けたサービスのリスト。
以下はチケットにリンクされたダッシュボードです:
サービスは Kubernetes 上で実行されており、サービスがメモリ制限に達し、クラッシュ (メモリを再利用) して再び開始するまで、時間の経過とともにメモリが蓄積されていることは明らかでした。
アプローチ
このセクションでは、私が目の前のタスクにどのように取り組み、メモリ使用率が高い原因を特定し、後でそれを修正したかを共有します。
コードを理解する
私はコードベースにあまり慣れていなかったので、まずコード、問題のライブラリが何をするのか、どのように使用されるのかを理解したいと思いました。このプロセスにより問題を特定しやすくなるだろうと期待していました。残念ながら、適切なドキュメントはありませんでしたが、コードを読み、サービスがライブラリをどのように利用しているかを検索することで、その要点を理解することができました。これは、Redis ストリームをラップし、イベントの生成と消費に便利なインターフェイスを公開するライブラリでした。コードを読むのに 1 日半かかりましたが、コードの構造と複雑さ (多くのクラス継承と rxjs が不慣れでした) のため、すべての詳細とデータの流れを把握することはできませんでした。
そこで、読むのを一時停止し、実際のコードを観察しながら問題を特定し、テレメトリ データを収集することにしました。
問題を個別に再現する
さらなる調査に役立つ利用可能なプロファイリング データ (連続プロファイリングなど) がなかったため、問題をローカルで再現し、メモリ プロファイルのキャプチャを試みることにしました。
Node.js でメモリ プロファイルをキャプチャする方法をいくつか見つけました。
- ヒープ スナップショットの使用
- ヒープ プロファイラーの使用
- パフォーマンスプロファイリング JavaScript
- クリニック.js
どこを見ればよいのか手がかりがなかったので、ライブラリの中で最も「データ集約型」と思われる部分、つまり Redis ストリームのプロデューサーとコンシューマーを実行することにしました。 Redis ストリームからデータを生成および消費する 2 つの単純なサービスを構築し、メモリ プロファイルをキャプチャして結果を経時的に比較する作業を進めました。残念ながら、サービスに負荷をかけてプロファイルを比較してから数時間後、2 つのサービスのいずれでもメモリ消費量の違いを見つけることができず、すべてが正常に見えました。このライブラリは、Redis ストリームと対話するためのさまざまなインターフェイスと方法を多数公開していました。特に実際のサービスに関するドメイン固有の知識が限られていると、問題を再現するのは予想よりも複雑になることが明らかになりました。
そこで問題は、メモリ リークを捕捉する適切な瞬間と条件をどのように見つけられるかということでした。
ステージング サービスからプロファイルをキャプチャする
前述したように、メモリ プロファイルをキャプチャする最も簡単で便利な方法は、影響を受ける実際のサービスに対して継続的にプロファイリングを行うことですが、私にはこのオプションはありませんでした。私は、追加の労力を必要とせずに必要なデータをキャプチャできるよう、少なくともステージング サービスを活用する方法 (同じように大量のメモリ消費に直面していました) を調査し始めました。
私は、Chrome DevTools を実行中のポッドの 1 つに接続し、経時的にヒープ スナップショットをキャプチャする方法を探し始めました。メモリ リークがステージングで発生していることはわかっていたので、そのデータをキャプチャできれば、少なくともいくつかのホットスポットを特定できるだろうと期待していました。驚いたことに、まさにそれを行う方法があります。
これを行うためのプロセス
- ポッド上のノード プロセスに SIGUSR1 シグナルを送信して、ポッド上の Node.js デバッガーを有効にします。
kubectl exec -it <nodejs-pod-name> -- kill -SIGUSR1 <node-process-id>
Node.js シグナルの詳細については、シグナル イベントを参照してください
成功した場合は、サービスからのログが表示されるはずです。
Debugger listening on ws://127.0.0.1:9229/.... For help, see: https://nodejs.org/en/docs/inspector
- 次のコマンドを実行して、デバッガがリッスンしているポートをローカルで公開します。
kubectl port-forward <nodejs-pod-name> 9229
- 前の手順で有効にしたデバッガーに Chrome Devtools を接続します。 chrome://inspect/ にアクセスすると、ターゲットのリストに Node.js プロセスが表示されるはずです。
そうでない場合は、ターゲット検出設定が適切に設定されていることを確認してください
これで、時間をかけてスナップショットのキャプチャを開始し (期間はメモリ リークが発生するまでに必要な時間によって異なります)、比較できるようになります。 Chrome DevTools は、これを行うための非常に便利な方法を提供します。
メモリ スナップショットと Chrome 開発ツールの詳細については、ヒープ スナップショットの記録
をご覧ください。スナップショットを作成すると、メインスレッドの他のすべての作業が停止します。ヒープの内容によっては、1 分以上かかる場合もあります。スナップショットはメモリに組み込まれるため、ヒープ サイズが 2 倍になり、メモリ全体がいっぱいになってアプリがクラッシュする可能性があります。
運用環境でヒープ スナップショットを取得する場合は、アプリケーションの可用性に影響を与えることなく、取得元のプロセスがクラッシュしても問題がないことを確認してください。
Node.js ドキュメントより
私の場合に戻り、比較するために 2 つのスナップショットを選択し、デルタで並べ替えると、以下に示すような結果が得られました。
最大のプラスの差分が文字列コンストラクターで発生していることがわかります。これは、サービスが 2 つのスナップショットの間に大量の文字列を作成したが、それらはまだ使用されていることを意味します。ここで問題となるのは、それらがどこで作成され、誰が参照しているのかということでした。キャプチャされたスナップショットにリテイナーと呼ばれるこの情報が含まれていて良かったです。
スナップショットと縮小することのない文字列リストを調べていると、ID に似ている文字列のパターンに気づきました。それらをクリックすると、それらを参照しているチェーン オブジェクト (別名リテイナー) が表示されます。ライブラリコードから認識できるクラス名からsentEventsという配列でした。さあ、犯人が決まりました。この時点では公開されていないと私が推測していた ID のリストは増え続けるばかりです。時間をかけて大量のスナップショットを撮影しましたが、大きなプラスのデルタを持つホットスポットとして繰り返し出現したのは、この 1 つの場所でした。
修正を確認する
この情報により、コード全体を理解しようとするのではなく、配列の目的、いつデータが設定され、いつクリアされるかに焦点を当てる必要がありました。コードが項目を配列にプッシュしていた場所が 1 か所あり、コードが項目をポップアウトしていた場所が 1 か所あり、修正の範囲が狭まっています。
配列が空になるべきときに空になっていなかったと考えても問題ありません。コードの詳細は省略しますが、基本的に何が起こっているかは次のとおりです:
- ライブラリは、イベントの消費、生成、またはイベントの生成と消費のためのインターフェイスを公開していました。
- イベントの消費と生成の両方を行っていた場合、イベントをスキップして再消費しないように、プロセス自体が生成したイベントを追跡する必要がありました。 sendEvents は生成時に設定され、消費時にメッセージがスキップされるとクリアされます。
これがどこへ向かうかわかりますか? ?サービスがイベントを生成するためだけにライブラリを使用していた場合でも、sentEvents にはすべてのイベントが設定されますが、それをクリアするためのコード パス (コンシューマ) がありませんでした。
プロデューサー、コンシューマー モードでのみイベントを追跡するようにコードにパッチを適用し、ステージングにデプロイしました。ステージング負荷があったとしても、このパッチが高いメモリ使用量の削減に役立ち、いかなるリグレッションも引き起こさないことは明らかでした。
結果
パッチが運用環境にデプロイされると、メモリ使用量が大幅に削減され、サービスの信頼性が向上しました (OOM がなくなりました)。
嬉しい副作用として、同じトラフィックを処理するのに必要なポッドの数が 50% 削減されました。
結論
これは、Node.js でのメモリ問題の追跡と、利用可能なツールにさらに慣れることに関して、私にとって素晴らしい学習機会でした。
各ツールの詳細については別の投稿に値するため、ここでは触れない方がよいと考えましたが、このトピックについて詳しく知りたい人や、同様の問題に直面している人にとって、これが良い出発点となることを願っています。
以上がNode.js での高いメモリ使用量を追跡するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

JavaScriptの最新トレンドには、TypeScriptの台頭、最新のフレームワークとライブラリの人気、WebAssemblyの適用が含まれます。将来の見通しは、より強力なタイプシステム、サーバー側のJavaScriptの開発、人工知能と機械学習の拡大、およびIoTおよびEDGEコンピューティングの可能性をカバーしています。

さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

JavaScriptは、現代のWeb開発のコア言語であり、その多様性と柔軟性に広く使用されています。 1)フロントエンド開発:DOM操作と最新のフレームワーク(React、Vue.JS、Angularなど)を通じて、動的なWebページとシングルページアプリケーションを構築します。 2)サーバー側の開発:node.jsは、非ブロッキングI/Oモデルを使用して、高い並行性とリアルタイムアプリケーションを処理します。 3)モバイルおよびデスクトップアプリケーション開発:クロスプラットフォーム開発は、反応および電子を通じて実現され、開発効率を向上させます。

この記事では、許可によって保護されたバックエンドとのフロントエンド統合を示し、next.jsを使用して機能的なedtech SaaSアプリケーションを構築します。 FrontEndはユーザーのアクセス許可を取得してUIの可視性を制御し、APIリクエストがロールベースに付着することを保証します

私はあなたの日常的な技術ツールを使用して機能的なマルチテナントSaaSアプリケーション(EDTECHアプリ)を作成しましたが、あなたは同じことをすることができます。 まず、マルチテナントSaaSアプリケーションとは何ですか? マルチテナントSaaSアプリケーションを使用すると、Singの複数の顧客にサービスを提供できます

C/CからJavaScriptへのシフトには、動的なタイピング、ゴミ収集、非同期プログラミングへの適応が必要です。 1)C/Cは、手動メモリ管理を必要とする静的に型付けられた言語であり、JavaScriptは動的に型付けされ、ごみ収集が自動的に処理されます。 2)C/Cはマシンコードにコンパイルする必要がありますが、JavaScriptは解釈言語です。 3)JavaScriptは、閉鎖、プロトタイプチェーン、約束などの概念を導入します。これにより、柔軟性と非同期プログラミング機能が向上します。
