Spring Cloud はマイクロサービス アーキテクチャを構築します: 分散サービス追跡 (コレクション原則)

Spring Cloud はマイクロサービス アーキテクチャを構築します: 分散サービス追跡 (コレクション原則)

このセクションの前に、Sleuth 追跡情報を導入し、レイテンシを分析および追跡するための Zipkin サーバーを構築する方法についてすでに詳しく説明しました。誰もが Sleuth と Zipkin についてある程度直感的に理解していると思います。次に、Sleuth がトレース情報を生成し、トレース情報を出力する全体的なプロセスと動作原理をよりよく理解できるように、Zipkin がトレース情報を収集するプロセスの詳細を紹介します。

[[226135]]

データモデル

まず、Zipkin での情報のトレースに関する基本的な概念を見てみましょう。 Zipkin の実装は Google の Dapper を利用しているため、主に次のような類似したコア用語があります。

  • スパン: 作業の基本単位を表します。 HTTP リクエストを例に挙げてみましょう。完全なリクエスト プロセスでは、クライアントとサーバーの両方で複数の異なるイベント状態が生成されます (以下で説明する 4 つのコア アノテーションによって示されるさまざまなステージなど)。同じリクエストの場合、それらは作業単位に属しているため、同じ HTTP リクエスト プロセス内の 4 つのアノテーションは同じ Span に属します。それぞれの異なる作業単位は、Span ID と呼ばれる 64 ビット ID によって一意に識別されます。また、他のワークユニットを直列に接続するためのIDがワークユニットに格納されます。また、Trace ID と呼ばれる 64 ビット ID によって一意に識別されます。同じリクエスト リンク内の異なるワーク ユニットには異なる Span ID が設定されますが、Trace ID は同じであるため、リクエスト内のすべての依存リクエストを Trace ID を介して連続して接続し、リクエスト リンクを形成できます。これら 2 つのコア ID に加えて、説明情報、イベント タイムスタンプ、注釈のキーと値のペアの属性、前のレベルの作業単位の Span ID など、他のいくつかの情報も Span に保存されます。
  • トレース: 同じトレース ID を持つ一連のスパンで形成されるツリー構造です。複雑な分散システムでは、通常、各外部要求によって複雑なツリー構造のトレースが生成されます。
  • 注釈: 時間内のイベントの存在を記録するために使用されます。アノテーションは、タイムスタンプを含むイベント タグと考えることができます。 HTTP リクエストの場合、Sleuth はリクエストの開始と終了を示す次の 4 つのコア アノテーションを定義します。

cs (クライアント送信): この注釈は、クライアントがリクエストを開始したことを記録するために使用されます。また、HTTP リクエストの開始も示します。

sr (サーバー受信): この注釈は、サーバーがリクエストを受信し、処理を開始する準備ができていることを記録するために使用されます。 2 つのアノテーション sr と cs のタイムスタンプの差を計算することで、現在の HTTP リクエストのネットワーク遅延を取得できます。

ss (サーバー送信): この注釈は、サーバーがリクエストを処理した後、リクエスト応答情報を送信する準備ができていることを記録するために使用されます。 2 つの注釈 ss と sr のタイムスタンプの差を計算することで、現在のサーバーがリクエストを処理するのに費やした時間を取得できます。

cr (クライアント受信): この注釈は、クライアントがサーバーから受信した応答を記録するために使用されます。また、HTTP リクエストの終了も示します。 2 つの注釈 cr と cs のタイムスタンプの差を計算することで、クライアントによる HTTP 要求の開始からサーバー応答の受信までに費やされた合計時間を取得できます。

  • BinaryAnnotation: 通常はキーと値のペアの形式で、追跡情報にいくつかの追加の補足説明を追加するために使用されます。例えば、HTTP リクエスト受信後の特定のビジネス ロジックの実行を記録する場合、イベントの状態を識別するためのデフォルトの Annotation はありませんが、それを補足する BinaryAnnotation 情報が存在します。

収集メカニズム

Zipkin の基本的な概念を理解した後、前の章で実装した例を組み合わせて、Spring Cloud Sleuth がリクエスト呼び出しリンクのトレース情報の生成、出力、および後続の処理をどのように完了するかを詳しく紹介して理解しましょう。

まず、リクエストが行われたときに Sleuth がどのようにトレース情報を記録して生成するかを見てみましょう。次の図は、この章で実装する例の実行プロセス全体を示しています。クライアントは trace-1 に HTTP リクエストを送信します。 trace-1 は trace-2 のサービスに依存しているため、trace-1 は trace-2 に別の HTTP 要求を送信します。 trace-2 が応答を返した後、trace-1 は応答結果を整理してクライアントに返します。

上図のリクエスト プロセスでは、呼び出しプロセス全体に 10 個のタグを付けました。これらのタグは、リクエスト リンクの操作中に記録されたいくつかの重要なイベント状態をそれぞれ表します。これらのタグには、イベントの時系列順に小さいものから大きいものまで番号を付けました。 1 はリクエストの開始を表し、10 はリクエストの終了を表します。各タグには、上で説明したコア要素の一部(トレース ID、スパン ID、注釈)が記録されます。これらのラベルはすべて 1 つのリクエストから生成されたものであるため、Trace ID は同じです。ラベル 1 とラベル 10 は開始ノードと終了ノードであり、それらの Trace ID と Span ID は同じです。

スパン ID によると、これらのタグには異なる ID を持つ合計 4 つのスパンが生成されていることがわかります。これら 4 つのスパンは次の 4 つの作業単位を表します。

  • スパン T: クライアント要求がトレース 1 に到着し、トレース 1 が要求応答を送信するという 2 つのイベントを記録します。クライアント要求応答プロセスの合計遅延時間を計算できます。
  • スパン A: クライアント要求を受信した後、処理メソッドを呼び出すトレース 1 アプリケーションの開始イベントと終了イベントを記録します。クライアント要求を処理するときに、trace-1 アプリケーションの内部ロジックによって費やされる時間遅延を計算できます。
  • スパン B: 4 つのイベントを記録します: トレース 1 アプリケーションがトレース 2 アプリケーションに要求を送信、トレース 2 アプリケーションが要求を受信し、トレース 2 アプリケーションが応答を送信、トレース 1 アプリケーションが応答を受信します。トレース 1 がトレース 2 を呼び出す全体的な依存時間 (cr - cs)、トレース 1 からトレース 2 へのネットワーク遅延 (sr - cs)、およびクライアント要求を処理するために使用されるトレース 2 アプリケーションの内部ロジックの時間遅延 (ss - sr) を計算できます。
  • スパン C: トレース 1 からの要求を受信した後、処理メソッドを呼び出すトレース 2 アプリケーションの開始イベントと終了イベントを記録します。トレース 1 からの要求を処理するときに、トレース 2 アプリケーションの内部ロジックによって費やされる時間遅延を計算できます。

図に示されている 4 つのスパンは、Zipkin トレースの詳細ページに表示される内容と正確に対応しています。それらの対応関係は次の図に示されています。

注意深い読者は次のような疑問を持つかもしれません。Zipkin サーバーで追跡情報を照会すると (下の図を参照)、照会結果ページに表示されるスパンは 5 で、クリックして追跡詳細ページに入ると、表示される合計スパンは 4 です。なぜスパンの数に矛盾があるのでしょうか。

実際、これら 2 つの側面のスパンの量と内容は異なる意味を持ちます。クエリ結果ページの 5 つのスパンは受信したスパンの合計数を表し、詳細ページの合計スパンは受信したスパンを結合した結果、つまり前回の記事で紹介した 4 つの異なる ID のスパン コンテンツを表します。次に、Zipkin サーバーがクライアント トレース情報を収集するプロセスを詳しく見て、受信する特定のスパン コンテンツを確認し、Zipkin で収集されるスパンの総数を把握しましょう。

Zipkin サーバーの収集プロセスをより直感的に観察するために、以前に実装したメッセージ ミドルウェア モードで追跡情報を収集するプログラムをデバッグすることができます。 Zipkin サーバーのメッセージ チャネル リスナーにブレークポイントを追加すると、クライアントが Zipkin サーバーに送信する情報を明確に確認できます。 spring-cloud-sleuth-zipkin-stream 依存パッケージにはコードがあまりありません。メッセージ チャネル リスナーを定義する実装クラスは簡単に見つかります: org.springframework.cloud.sleuth.zipkin.stream.ZipkinMessageListener。具体的な実装は次のとおりです。SleuthSink.INPUT はリスニング入力チャネルを定義します。デフォルトでは、sleuth という名前のトピックが使用されますが、これは Spring Cloud Stream 構成を通じて変更することもできます。

  1. @メッセージエンドポイント
  2. @条件付き(NotSleuthStreamClient.クラス)
  3. パブリッククラス ZipkinMessageListener {
  4.    
  5. 最終コレクター コレクター;
  6.  
  7. @ServiceActivator(入力チャネル = SleuthSink.INPUT)
  8. パブリックvoid シンク(Spans 入力) {
  9. List<zipkin.Span> を ConvertToZipkinSpanList に変換します。変換(入力);
  10. this.collector.accept(変換済み、Callback.NOOP);
  11. }
  12.      
  13. ...
  14.  
  15. }

チャネル監視方法の定義から、Sleuth と Zipkin を統合すると 2 つの異なる Span 定義があることがわかります。 1 つは、メッセージ チャネルの入力オブジェクト org.springframework.cloud.sleuth.stream.Spans です。これは、メッセージ チャネル送信用に sleuth で定義された Span オブジェクトです。各メッセージに含まれる Span 情報は org.springframework.cloud.sleuth.Span オブジェクトで定義されていますが、実際に zipkin サーバーで使用される Span オブジェクトはこの Span オブジェクトではなく、zipkin 独自の zipkin.Span オブジェクトです。したがって、メッセージ チャネル監視処理方法では、Sleuth Span が処理され、Sleuth Span が受信されるたびに Zipkin Span に変換されます。

次に、sink(Spans input) メソッド実装のコードの最初の行にブレークポイントを追加し、トレース情報を RabbitMQ に送信するようにトリガーする要求を trace-1 に送信してみます。この時点で、DEBUG モードを通じて、メッセージ チャネルがトレース 1 から 1 つ、トレース 2 から 1 つの 2 つの入力を受信したことがわかります。次の 2 つの図は、それぞれトレース 1 とトレース 2 からのトレース メッセージを示しています。トレース 1 のトレース メッセージには 3 つのスパン情報が含まれ、トレース 2 のトレース メッセージには 2 つのスパン情報が含まれます。したがって、このリクエスト呼び出しチェーンでは合計 5 つのスパン情報が送信されます。これは、Zipkin 検索結果ページに表示されるスパンの数です。

特定の Span コンテンツをクリックすると、以下に示す構造が表示されます。この構造には、開始時刻、終了時刻、Span の名前、トレース ID、Span ID、タグ (Zipkin の BinaryAnnotation に対応)、ログ (Zipkin の Annotation に対応)、および前述のその他のコア トラッキング情報など、Sleuth で定義された Span の詳細情報を記録します。

この時点で、注意深い読者は疑問を持つかもしれません。詳細情報に表示される Trace ID と Span ID の値が、リストに表示される概要情報の値と異なるのはなぜですか。実際、Trace ID と Span ID は両方とも long 型を使用して保存されます。 DEBUG モードで詳細を表示すると、当然 long 型、つまり元の値になります。ただし、Span オブジェクトを表示すると、toString() 関数によって処理された値が表示されます。 sleuth の Span ソースコードから、次の定義を確認できます。 Trace IDとSpan IDを出力する際に​​、idToHex関数を呼び出してlong型の値を16進数の文字列値に変換するため、DEBUGでは2つの異なる値が表示されます。

  1. パブリック文字列toString() {
  2. 戻る  "[トレース: " + idToHex(this.traceId) + "、スパン: " + idToHex(this.spanId)
  3. + "、親: " + getParentIdIfPresent() + "、エクスポート可能:" + this.exportable + "]" ;
  4. }
  5.  
  6. 公共 静的文字列idToHex(long id) {
  7. Long.toHexString(id)を返します
  8. }

Sleuth を受け取った後、プログラムの実行を続けると、converted というリストに保存されている変換された Zipkin Span コンテンツを確認できます。具体的な内容は以下のとおりです。

上の図は、変換された Zipkin Span オブジェクトの詳細を示しています。これまでに紹介した Zipkin の基本概念である、よく知られた名前が数多く見られます。これらの基本概念の価値は、探偵の本来のスパンにも見出すことができます。その中でも、アノテーションとバイナリアノテーションは少し特殊です。 sleuth によって定義された範囲内で同じ名前は使用されません。代わりに、ログとタグを使用して名前を付けます。ここの詳細な情報から、アノテーションとバイナリアノテーションの役割を直感的に理解できます。アノテーションには、現在の Span に含まれるさまざまなイベント状態と、対応するイベント状態のタイムスタンプが格納され、バイナリ アノテーションには、イベントに関する補足情報が格納されます。たとえば、HTTP リクエストの詳細な説明は上図に保存されます。さらに、呼び出し関数の詳細な説明を保存することもできます(下の図を参照)。

次に、デバッグ メッセージ リスナーが受信した 5 つのスパンの内容を詳しく見てみましょう。まず、各 Span には 3 つの ID 情報が含まれていることがわかります。 Span 自体を識別する ID とリンク全体を識別するために使用される traceId に加えて、これまでに説明されていない parentId もあります。各 Span の親子関係を識別するために使用される ID です (値は前の実行単位の Span の ID から取得されます)。 parentId の定義を通じて、各 Span の前身ノードを見つけることができ、それによってリクエスト呼び出しチェーン内の各 Span の正確な位置を特定できます。各呼び出しチェーンには、parentId が null である特別な Span が存在します。このタイプの Span をルート Span と呼びます。これは、このリクエスト呼び出しチェーンのルート ノードです。これらのスパン間の関係を理解するために、ルート スパンから始めて、リンク全体のスパンのコンテンツを整理することができます。次の表は、ルート スパンから始めて、各スパンの親子関係に基づいて分類した結果を示しています。

上記の表のホストは、Span の送信元のアプリケーションを表します。 Span ID は現在の Span の一意の識別子です。親スパン ID は、前の実行ユニットのスパン ID を表します。アノテーションは、Span に記録されたイベントを表します (ここでは主に HTTP リクエストの 4 つのステージを記録するために使用されます。テーブルの内容は省略され、アノテーション名のみが記録されます (sr はリクエストを受信するサーバー、ss はリクエストを送信するサーバー、cs はリクエストを送信するクライアント、cr はリクエストを受信するクライアントを表します)。サービス名、タイムスタンプ、IP アドレス、ポート番号など、その他の詳細は省略されています)。バイナリ アノテーションは、イベントの補足情報を表します (Sleuth の元の Span レコードはより詳細ですが、Zipkin の Span は処理後に一部のコンテンツを削除します。また、アノテーション識別子を持つ情報については、バイナリ アノテーションは補足に使用されなくなりました。上記の表では、サービス名、クラス名、メソッド名のみを記録し、タイムスタンプ、IP アドレス、ポート番号などの他の情報も省略しています)。

収集された Zipkin Span の詳細を通じて、このセクションの冒頭で紹介した呼び出しリンクの 10 個のラベル コンテンツと簡単に関連付けることができます。

  • Span ID = T のタグが 2 つあり、それぞれ 1 と 10 の番号が付けられています。これらは、このリクエストの開始と終了を示します。これらは、上記の表の ID e9a933ec50d180d6 のスパンに相当します。タグ 10 が実行された後、trace-1 はタグ 1 と 10 を 1 つのスパンにマージし、Zipkin サーバーに送信します。
  • Span ID = A のラベルは 2 つあり、シリアル番号は 2 と 9 です。これらはそれぞれ、trace-1 要求を受信した後、特定の処理メソッド呼び出しの開始と終了を表します。タグ 9 が実行された後、trace-1 はタグ 2 と 9 を 1 つのスパンにマージし、Zipkin サーバーに送信します。
  • スパンの ID = B には、3、4、7、8 の 4 つのタグがあります。このスパンは、2 つのインスタンスにまたがるという点で特別です。タグ 3 と 8 はトレース 1 によって生成され、タグ 4 と 7 はトレース 2 によって生成されます。したがって、タグは 2 つの span コンテンツに分割され、Zipkin サーバーに送信されます。タグ 8 が終了すると、trace-1 はタグ 3 と 8 を 1 つのスパンにマージして Zipkin サーバーに送信し、trace-2 はタグ 4 と 7 を 1 つのスパンにマージして、タグ 7 が終了すると Zipkin サーバーに送信します。
  • Span ID = C のラベルは 2 つあり、シリアル番号は 5 と 6 です。これらはそれぞれ、trace-2 要求を受信した後、特定の処理メソッド呼び出しの開始と終了を表します。タグ 6 が実行された後、trace-2 はタグ 2 と 9 を 1 つのスパンにマージし、Zipkin サーバーに送信します。

したがって、上記の分析によれば、Zipkin は合計 5 つのスパンを受け取ります。1 つのスパン T、1 つのスパン A、2 つのスパン B、および 1 つのスパン C です。前のリクエスト リンクのラベル図とここでのスパン レコードを組み合わせると、次の図に示すようにスパン収集プロセスをまとめることができます。読者はこの図を参照して、スパン収集プロセスの処理ロジックと各スパン間の関係を理解できます。

Zipkin サーバーは 5 つのスパンを受信しますが、前回の記事で分析したように、スパン ID = B のラベルが 2 つあります。これらは同じ HTTP リクエスト (trace-1 が trace-2 のサービスを呼び出します) からのものであるため、概念的には同じ作業単位に属します。したがって、Zipkin サーバーは、フロントエンドで分析の詳細を表示するときに、これら 2 つのスパンを結合します。マージされたスパンの数は、リクエスト リンクの詳細ページの合計スパンの数です。

次の図は、この章の例のリクエスト リンクの詳細ページです。このページには、各スパンのレイテンシ統計が表示されます。 3 番目のスパン情報は、トレース 1 からトレース 2 への HTTP 要求呼び出しです。スパンをクリックすると、そのスパンの詳細情報を表示できます。クリックすると、スパンの詳細がモーダル ボックスにポップアップ表示されます (図の下半分を参照)。ポップアップ ボックスには、スパンの Annotation および BinaryAnnotation 情報が詳細に表示されます。 Annotation 領域には、trace-1 と trace-2 によって同時に送信された Span 情報が含まれていることがわかります。一方、BinaryAnnotation 領域には、HTTP リクエストの詳細情報が表示されます。

完全な例:

読者は、自分の好みに応じて、次の 2 つのリポジトリで trace-1 プロジェクトと trace-2 プロジェクトを表示することを選択できます。

Github: https://github.com/dyc87112/SpringCloud-Learning/

gitee: https://gitee.com/didispace/SpringCloud-Learning/

【この記事は51CTOコラムニスト「Zhai Yongchao」によるオリジナル記事です。転載の許可を得るには、51CTO を通じて著者に連絡してください。

この著者の他の記事を読むにはここをクリックしてください

<<:  企業にとってのハイブリッドおよびマルチクラウド コンピューティング モデルの利点

>>:  InspurとOdooが中小企業向けSaaS市場を共同で開発する合弁会社を設立

推薦する

入札プロモーションリンクは、Baiduが外部リンクを知るための新しいチャネルではない

今日、ウェブマスターのウェブサイトで「Baidu Knowsの新しい外部リンクチャネル:プロモーショ...

Kubernetes の混乱から熟練へ: クラスター サービスの 3 つの重要なポイントと 1 つの実装

私の経験では、Kubernetes クラスター サービスの概念を理解するのは簡単ではありません。特に...

マイクロソフトがInternet Explorer 11をリリース

11 月 8 日、Microsoft Internet Explorer 11 が正式にリリースされ...

ギフト ウェブサイトは、ユーザーに最高の体験を提供するためにコンテンツをどのように最適化すればよいでしょうか?

ウェブサイトの最適化を行うすべてのウェブマスターは、モール型ウェブサイトを迅速に発展させたい場合、ウ...

より効率的な情報のためのウェブサイトのコピーライティングデザインについての簡単な説明

以前、北京で開催されたインタラクティブ体験デーで、私は新浪のプロダクトマネージャー、李啓明氏の「より...

将来、土地をどのように耕作すべきでしょうか?豚の飼育方法は?

[51CTO.com オリジナル記事] 農業や豚の飼育と聞いて、何を思い浮かべますか?地球を向いて、...

パブリッククラウド、プライベートクラウド、ハイブリッドクラウド、クラウド災害復旧にはどれを選択すべきでしょうか?

パブリック クラウド、プライベート クラウド、ハイブリッド クラウドのいずれをベースとしているかにか...

工業情報化省のAPP申請要件は、実施の難しさに関する懸念を引き起こしている。

12月11日、工業情報化部がAPP登録を実施する予定であるというニュースがモバイルインターネット業界...

ユーザー維持を左右する重要な要素は、製品、ユーザー チャネル、運用戦略の 3 つです。

今日のAPP爆発の時代では、栄光も衰退も予想外のものとなりました。一夜にして有名になるか、一夜にして...

タイトル: サイト最適化の主な操作

初心者でもベテランでも、タイトルが SEO 最適化において重要な役割を果たすことはご存じでしょう。そ...

マーケティングにおける「存在感」の重要性を例を通して簡単に分析する

地球上の誰もが知っているチャイナモバイルやチャイナテレコムのような企業は、なぜ莫大な費用を広告に費や...

無料のモバイルウェブサイトセルフサービスウェブサイト構築プラットフォームの推奨

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っていますかつては、...

史上最も完全かつ詳細な APP プロモーション チャネルと計画スキーム

アプリケーションを成功させるには、APP の開発は最初のステップにすぎません。それよりも重要なのは、...

ウェブサイト構築の難点は実は「程度」の問題

ウェブサイト構築の過程では、動的な画像がない、インタラクティブなデザインがシンプルであるなど、それほ...

Flashサイトの最適化の難しさに関する4つの簡単な説明

Flash サイトは、おそらく最適化担当者にとって最も厄介なタイプのサイトの 1 つです。 Goog...