エラー状態を再現し、アプリケーションの実行中に何が起こっているかを表示するにはどうすればよいですか?
アプリケーションの異なる並行部分間の相互作用をどのように視覚化しますか?
私の経験に基づいて、これらの2つの側面に対する答えは次のとおりです。
分散トレース
分散トレースは、システムの個々の同時コンポーネントのタイミングデータをキャプチャし、グラフィカル形式で表示する技術です。同時実行の表現は常にインターリーブされ、並列で実行されているものとそうでないものを確認できます。
分散トレースは、(もちろん)非同期システムであり、高度に同時実行される(もちろん)分散システムに由来しています。分散トレースを備えた分散システムにより、人々は次のことが可能になります。
a)重要なボトルネックを特定し、b)アプリケーションの理想的な「実行」の視覚的表現を取得し、c)実行されている同時動作の可視性を提供し、d)変更の違いを評価するために使用できるタイミングデータを取得しますシステム(強力なSLAがある場合は非常に重要です)。
ただし、分散トレースの結果は次のとおりです。
ネットワークを介して実行および送信する可能性のあるコードが増えるため、すべての同時プロセスにオーバーヘッドが追加されます。場合によっては、このオーバーヘッドは非常に大きくなります。Googleでも、ユーザーエクスペリエンスを損なわないように、すべてのリクエストの小さなサブセットに対してのみトレースシステムDapperを使用します。
多くの異なるツールが存在しますが、それらのすべてが相互運用可能であるわけではありません。これはOpenTracingなどの標準によって多少改善されていますが、完全には解決されていません。
共有リソースとその現在のステータスについては何もわかりません。アプリケーションコードと表示されているグラフに基づいて推測できる場合がありますが、この点では有用なツールではありません。
現在のツールでは、予備のメモリとストレージがあると想定しています。制約によっては、timeseriesサーバーのホスティングは安くない場合があります。
エラー追跡ソフトウェア
上記のSentryにリンクしているのは主に、最も広く使用されているツールであり、正当な理由により、Sentryのようなエラー追跡ソフトウェアがランタイム実行をハイジャックして、発生したエラーのスタックトレースを中央サーバーに同時に転送するためです。
並行コードにおけるこのような専用ソフトウェアの最終的な利点:
- 重複するエラーは複製されません。つまり、1つ以上の同時実行システムで同じ例外が発生した場合、Sentryはインシデントレポートをインクリメントしますが、インシデントの2つのコピーは送信しません。
つまり、無数の同時エラーレポートを実行することなく、どの同時システムでどの種類のエラーが発生しているかを把握できます。分散システムからの電子メールスパムに苦しんだことがあるなら、あなたは地獄がどんな感じか知っているでしょう。
同時実行システムのさまざまな側面を「タグ付け」することもできます(ただし、スレッドはタスク間を効率的にジャンプするだけで、イベントハンドラーを処理する必要があるため、厳密には1つのスレッドで作業がインターリーブされていないことが前提です)完了まで)、タグごとのエラーの内訳をご覧ください。
- このエラー処理ソフトウェアを変更して、ランタイム例外の詳細を追加できます。プロセスにはどのようなオープンリソースがありましたか?このプロセスが保持していた共有リソースはありますか?この問題が発生したのはどのユーザーですか?
これにより、細心のスタックトレース(ファイルの縮小バージョンを提供する必要がある場合はソースマップ)に加えて、大部分の問題を簡単に特定できます。
- (Sentry固有)システムのテスト実行用に別個のSentryレポートダッシュボードを使用して、テストのエラーをキャッチできます。
このようなソフトウェアの欠点は次のとおりです。
すべてのように、彼らはバルクを追加します。たとえば、このようなシステムを組み込みハードウェア上に配置したくない場合があります。このようなソフトウェアの試用を行って、アイドルマシンで数百回の実行をサンプリングした場合としない場合の単純な実行を比較することを強くお勧めします。
これらのシステムの多くは暗黙的に例外をキャッチすることに依存しているため、すべての言語が等しくサポートされているわけではなく、すべての言語が堅牢な例外を備えているわけではありません。そうは言っても、多くのシステムのクライアントがいます。
これらのシステムの多くは本質的にクローズドソースであるため、セキュリティリスクとして発生する可能性があります。そのような場合は、それらを調査するためにデューデリジェンスを行うか、必要に応じて自分でロールバックしてください。
必要な情報が常に提供されるとは限りません。これは、可視性を追加しようとするすべての試みのリスクです。
これらのサービスのほとんどは、高度な同時Webアプリケーション向けに設計されているため、すべてのツールがユースケースに最適とは限りません。
要するに:可視性を有する任意の並行システムの最も重要な部分です。上記の2つの方法は、ハードウェアとデータに関する専用のダッシュボードと組み合わせて、特定の時点でシステムの全体像を把握し、その側面に正確に対応するために業界全体で広く使用されています。
追加の提案
ひどい方法で同時発生の問題を解決しようとした人たちがコードを修正するのを気にするよりも多くの時間を費やした 毎回、次のことが開発者のエクスペリエンス(ユーザーエクスペリエンスと同じくらい重要です)を大幅に改善できる場合があります。
優れたリンクテストでは、あるコンポーネントが別のコンポーネントと単独で通信するときに、受信したメッセージと送信したメッセージが同じaaであるかどうかを確認します。共有サービスに依存して通信する2つ以上のコンポーネントがある場合は、それらをすべて起動し、中央サービスを介してメッセージを交換し、すべてが最終的に期待どおりのものかどうかを確認します。
多くのコンポーネントを含むテストをコンポーネント自体のテストと各コンポーネントの通信方法のテストに分割すると、コードの妥当性に対する信頼性が高まります。このように厳密なテストを行うことで、サービス間で契約を実施できるだけでなく、一度に実行されているときに発生する予期しないエラーをキャッチできます。
- 適切なアルゴリズムを使用して、アプリケーションの状態を検証します。すべてのワーカーがタスクを完了するのを待機しているマスタープロセスがあり、すべてのワーカーが完全に完了した場合にのみ次のステップに移動したい場合など、単純なことについて話します-これはグローバル検出の例ですSafraのアルゴリズムなどの既知の方法論が存在する終端。
これらのツールの一部は言語にバンドルされています-たとえば、Rustはコンパイル時にコードに競合状態がないことを保証しますが、Goはコンパイル時にも実行される組み込みのデッドロック検出機能を備えています。問題がプロダクションにヒットする前にキャッチできれば、常に勝ちです。
一般的な経験則:並行システムの障害に対する設計。一般的なサービスがクラッシュまたは破損することを予測します。これは、マシン間で分散されていないコードにも当てはまります。単一のマシン上の同時コードは、いつでも消えたり削除されたりする可能性のある外部依存関係(共有ログファイル、Redisサーバー、いまいましいMySQLサーバーなど)に依存する可能性があります。
これを行う最善の方法は、アプリケーションの状態を時々検証することです。各サービスのヘルスチェックを行い、そのサービスのコンシューマーにヘルスが悪いことを通知するようにします。Dockerのような最新のコンテナツールはこれを非常にうまく行うため、サンドボックス処理に利用する必要があります。
並行処理できるものと順次処理できるものをどのように把握しますか?
高度な並行システムでの作業で学んだ最大の教訓の1つは、これです。十分なメトリックを取得できないことです。メトリックは、アプリケーションのすべてを完全に駆動するものである必要があります。すべてを測定するのでなければ、あなたはエンジニアではありません。
メトリックがなければ、いくつかの非常に重要なことを実行できません。
システムへの変更によって生じた違いを評価します。チューニングノブAがメトリックBを上げ、メトリックCを下げるかどうかがわからない場合、人々がシステムに予期しない悪性コードをプッシュしたときにシステムを修正する方法がわかりません(そして、システムにコードをプッシュします) 。
物事を改善するために次に何をする必要があるかを理解してください。アプリケーションのメモリが不足していることを知るまで、メモリを増やす必要があるか、サーバー用にディスクを購入する必要があるかを判別できません。
メトリックは非常に重要で不可欠なので、システムが必要とするものについて考える前に、測定したいものを計画するための意識的な努力をしました。実際には、メトリックは、私が信じていることをとても重要である、彼らはこの質問に対する正しい答えです:あなただけの時に、順次または同時行うことができるものを知っている測定プログラム内のビットが何をしていますか。適切な設計では、推測ではなく数字を使用します。
そうは言っても、確かにいくつかの経験則があります。
シーケンシャルは依存性を意味します。1つのプロセスが何らかの方法で他のプロセスに依存している場合、2つのプロセスは連続している必要があります。依存関係のないプロセスは並行でなければなりません。ただし、ダウンストリームのプロセスが無期限に待機することを妨げない、上流の障害を処理する方法を計画します。
I / Oにバインドされたタスクと同じコア上のCPUにバインドされたタスクを混在させないでください。(たとえば)同じスレッドで10の同時リクエストを起動し、入ってくるとすぐにそれらをスクレイピングし、500に拡張することを期待するWebクローラーを作成しないでください-I / Oリクエストは並列にキューに送られますが、 CPUは引き続きそれらを順番に通過します。(このシングルスレッドのイベント駆動モデルは人気がありますが、この側面のために制限されています-これを理解するのではなく、人々は単に手を挙げて、ノードをスケーリングしないと言います)
1つのスレッドで多くのI / O作業を実行できます。ただし、ハードウェアの同時実行性を完全に使用するには、すべてのコアを占有するスレッドプールを使用します。上記の例では、CPU作業のためだけに5つのPythonプロセス(それぞれが6コアマシンのコアを使用可能)とI / O作業のために6番目のPythonスレッドを起動すると、予想よりはるかに高速にスケーリングします。
CPUの同時実行性を利用する唯一の方法は、専用のスレッドプールを使用することです。多くの場合、単一のスレッドで十分なI / Oバウンド作業に十分です。これが、Nginxのようなイベント駆動型WebサーバーがApache(CPUを必要とするものでI / Oバウンド作業を制限し、リクエストごとにプロセスを起動する)よりも優れている(純粋にI / Oバウンド作業を行う)理由ですが、Nodeを使用して実行する理由です数万のGPU計算を並行して受信するのはひどい考えです。