著者|羅淳武
ソース| IT担当者の高度な職場(ID:BestITer)
上記の監視チャートは、サーバー側のR&D学生にはおなじみです。日常のシステムメンテナンスでは、「サービスタイムアウト」は、最も多くのアラームを監視する問題のタイプに属している必要があります。
特にマイクロサービスアーキテクチャでは、リクエストが非常に長いリンクを経由する必要がある場合があり、結果は複数のサービス呼び出しの後でのみ返すことができます。サービスタイムアウトが発生すると、R&Dの学生は多くの場合、自分のシステムのパフォーマンスを分析し、サービスのパフォーマンスに依存する必要があります。そのため、サービスエラーや異常なサービスコールよりもサービスタイムアウトを調査することは困難です。
この記事では、実際のオンライン事故を体系的に紹介します。マイクロサービスアーキテクチャの下で、RPCインターフェイスのタイムアウト期間を正しく理解して設定し、サーバー側インターフェイスを開発するときに誰もがよりグローバルな視点を持つようにする方法です。コンテンツは、次の4つの部分に分かれています。
RPCインターフェースのタイムアウトが原因のオンライン事故から
タイムアウトの実装原理は何ですか?
タイムアウトは解決するためにどのような問題を設定していますか?
適切なタイムアウト期間を設定するにはどうすればよいですか?
オンライン事故から話す
事故はeコマースAPPのホームページ推奨モジュールで発生し、ある日の正午に突然ユーザーのフィードバックを受け取りました。APPホームページのバナーマップとナビゲーション領域を除いて、以下の推奨モジュールは空白のページになりました(推奨モジュールはホームページスペースの2/3を占めています)これは、ユーザーの興味に従ってリアルタイムでアルゴリズムによって推奨される製品のリストです。
上記のビジネスシナリオは、次のコールチェーンを使用して理解できます。
APPがサービスゲートウェイへのHTTP要求を開始します
サービスゲートウェイRPCは推奨サービスを呼び出し、推奨製品のリストを取得します
手順2で呼び出しが失敗した場合、サービスは低下し、RPCに変更されて商品仕分けサービスを呼び出し、売れ筋商品のリストを取得します。
手順3で呼び出しが失敗した場合は、再度ダウングレードして、Redisキャッシュ内のホット製品のリストを直接取得します。
一見すると、サービスに依存するダウングレード戦略の両方が考慮されます。理論的には、推奨サービスまたは商品注文サービスがすべて停止しても、サーバーはAPPにデータを返すことができるはずです。ただし、APP側の推奨モジュールは実際には空白であり、ダウングレード戦略は効果的でない場合があります。以下では、ポジショニングプロセスについて詳しく説明します。
1.問題の特定プロセス
ステップ1:APP側は、パケットキャプチャを通じて、HTTPリクエストにインターフェースタイムアウトがあることを検出します(タイムアウト期間は5秒に設定されています)。
手順2:ログからサービスゲートウェイが、推奨されるサービスを呼び出すRPCインターフェイスでタイムアウトが発生したことを検出しました(タイムアウト時間は3秒に設定されています)。エラーメッセージは次のとおりです。
手順3:ログから見つかった推奨サービス:ダボのスレッドプールが使い果たされ、エラーメッセージは次のとおりです。
上記の3つのステップを通じて、問題は基本的にレコメンデーションサービスにありました。レコメンデーションサービスが依存するredisクラスターが利用できないためにタイムアウトが発生し、スレッドプールが使い果たされたことが原因であることがさらに調査されました。詳細な理由はここでは展開されておらず、この記事で説明するトピックには関係ありません。
2.ダウングレード戦略が実施されなかった理由の分析
以下の分析を続けましょう:推奨サービス呼び出しが失敗したときに、サービスゲートウェイのダウングレード戦略が有効にならないのはなぜですか?理論的には、バッキングのために商品注文サービスを呼び出すためにダウングレードすべきではないでしょうか?
最終的な追跡分析により根本的な原因が見つかりました:APPがビジネスゲートウェイを呼び出すタイムアウト時間は5秒、ビジネスゲートウェイが推奨サービスを呼び出すタイムアウト時間は3秒であり、3つのタイムアウト再試行を設定して、推奨サービス呼び出しが失敗した場合、最初の2回再試行すると、HTTP要求がタイムアウトしたため、サービスゲートウェイのすべてのダウングレードポリシーは有効になりません。以下は、より直感的な回路図です。
3.ソリューション
サービスゲートウェイが推奨サービスを呼び出すためのタイムアウト時間を800ミリ秒に変更(推奨サービスTP99は約540ミリ秒)し、タイムアウト再試行時間を2に変更しました。
サービスゲートウェイが商品注文サービスを呼び出すためのタイムアウト期間を600ミリ秒に変更し(商品注文サービスのTP99は約400ミリ秒)、タイムアウトの再試行回数を2に変更しました。
タイムアウト時間と再試行回数の設定については、コールチェーン全体でのすべての依存サービスの時間消費や、各サービスがコアサービスであるかどうかなど、多くの要素を考慮する必要があります。ここでは始めません。具体的な方法については後で詳しく説明します。
タイムアウトの実装原理は何ですか?
RPCフレームワークのタイムアウトの原理を理解することによってのみ、RPCフレームワークをより適切に設定できます。ダボ、SpringCloud、または大手メーカー(JDのJSFなど)によって開発されたマイクロサービスフレームワークのいずれであっても、タイムアウトの実装原理は基本的に似ています。Dubbo 2.8.4のソースコードを例にして、具体的な実装を見てみましょう。
ダボに詳しい学生は、タイムアウト期間をプロバイダー(サーバー、サービスプロバイダー)とコンシューマー(コンシューマー、サービス呼び出し元)の2か所で構成できることを知っています。サーバーのタイムアウト構成は、コンシューマーのデフォルト構成です。つまり、サーバーがタイムアウト期間を設定している限り、すべてのコンシューマーがタイムアウト期間を設定する必要はなく、登録センターを介してコンシューマーに渡すことができます。サーバーはインターフェースのパフォーマンスをより認識しているため、サーバーはサーバーに任せて構成するのが妥当です。
Dubboは、メソッドレベル、インターフェースレベル、グローバルなど、非常に細かいタイムアウト設定をサポートしています。すべてのレベルが同時に構成されている場合、優先順位は次のとおりです:コンシューマーメソッドレベル>サーバーメソッドレベル>コンシューマーインターフェイスレベル>サーバーインターフェイスレベル>コンシューマーグローバル>サーバーグローバル。
ソースコードを通して、最初にサーバーのタイムアウト処理ロジックを調べます
1public class TimeoutFilter implements Filter { 2 3 public TimeoutFilter() { 4 } 5 6 public Result invoke(...) throws RpcException { 7 // 执行真正的逻辑调用,并统计耗时 8 long start = System.currentTimeMillis(); 9 Result result = invoker.invoke(invocation);10 long elapsed = System.currentTimeMillis() - start;1112 // 判断是否超时13 if (invoker.getUrl() != null && elapsed > timeout) {14 // 打印warn日志15 logger.warn("invoke time out...");16 }1718 return result;19 }20}
サーバーがタイムアウトした場合でも、警告ログのみが出力されることがわかります。したがって、サーバーのタイムアウト設定は実際の呼び出しプロセスには影響せず、タイムアウトになっても、処理ロジック全体が実行されます。
コンシューマー側のタイムアウト処理ロジックを見てみましょう
1public class FailoverClusterInvoker { 2 3 public Result doInvoke(...) { 4 ... 5 // 循环调用设定的重试次数 6 for (int i = 0; i < retryTimes; ++i) { 7 ... 8 try { 9 Result result = invoker.invoke(invocation);10 return result;11 } catch (RpcException e) {12 // 如果是业务异常,终止重试13 if (e.isBiz()) {14 throw e;15 }1617 le = e;18 } catch (Throwable e) {19 le = new RpcException(...);20 } finally {21 ...22 }23 }2425 throw new RpcException("...");26 }27}
FailoverClusterは、クラスターフォールトトレランスのデフォルトモードです。呼び出しが失敗すると、他のサーバーの呼び出しに切り替わります。再度doInvokeメソッドを確認します。呼び出しが失敗すると、最初にビジネス例外かどうかを判断し、そうであれば再試行を終了します。それ以外の場合は、再試行回数に達するまで再試行を続けます。
invokerのinvokeメソッドを続けます。リクエストが発行された後、Futureのgetメソッドを通じて結果が取得されていることがわかります。ソースコードは次のとおりです。
1public Object get(int timeout) { 2 if (timeout <= 0) { 3 timeout = 1000; 4 } 5 6 if (!isDone()) { 7 long start = System.currentTimeMillis(); 8 this.lock.lock(); 910 try {11 // 循环判断12 while(!isDone()) {13 // 放弃锁,进入等待状态14 done.await((long)timeout, TimeUnit.MILLISECONDS);1516 // 判断是否已经返回结果或者已经超时17 long elapsed = System.currentTimeMillis() - start;18 if (isDone() || elapsed > (long)timeout) {19 break;20 }21 }22 } catch (InterruptedException var8) {23 throw new RuntimeException(var8);24 } finally {25 this.lock.unlock();26 }2728 if (!isDone()) {29 // 如果未返回结果,则抛出超时异常30 throw new TimeoutException(...);31 }32 }3334 return returnFromResponse();35 }
メソッドに入るとタイマーがスタートし、設定したタイムアウト時間内に返却結果が得られない場合は、TimeoutExceptionがスローされます。したがって、コンシューマのタイムアウトロジックは、タイムアウト時間とタイムアウトの数(ネットワーク例外や応答タイムアウトなど)の2つのパラメータによって制御され、再試行回数に達するまで常に再試行します。
解決するために設定されたタイムアウトの問題
RPCフレームワークのタイムアウト再試行メカニズムはどのような問題を解決しますか?マイクロサービスアーキテクチャのマクロの観点からは、サービスリンクの安定性を確保し、フレームワークレベルのフォールトトレランスを提供することです。微視的にそれを理解するには?次の特定のケースから見ることができます:
1.コンシューマーがプロバイダーを呼び出すタイムアウトが設定されていない場合、コンシューマーの応答時間はプロバイダーの応答時間よりも確実に長くなります。プロバイダーのパフォーマンスが低下すると、プロバイダーの復帰を無期限に待機する必要があるため、コンシューマーのパフォーマンスも影響を受けます。コールリンク全体が複数のサービスA、B、C、およびDを経由する場合、Dのパフォーマンスが低下する限り、A、B、Cに下から上に影響し、最終的にリンク全体がタイムアウトするか、麻痺する可能性があるため、設定します。タイムアウト期間は非常に必要です。
2.消費者をコア商品サービス、プロバイダーを非コアレビューサービスと仮定すると、評価サービスにパフォーマンスの問題がある場合、商品サービスは評価情報の返却を受け入れることができないため、外部サービスを引き続き提供できます。この場合、タイムアウト時間を設定する必要があり、評価サービスがこのしきい値を超えた場合、商品サービスは待機し続ける必要はありません。
3.プロバイダーの可能性が最も高いのは、一時的なネットワークジッタまたは高いマシン負荷によるタイムアウトです。タイムアウトの直後に中止すると、一部のシナリオでビジネス上の損失が発生します(在庫インターフェースのタイムアウトなどにより注文が失敗するなど)。したがって、この一時的なサービスジッタについては、タイムアウト後に再試行すると保存できるため、再試行メカニズムを使用して解決する必要があります。
しかし、タイムアウト再試行メカニズムを導入した後、すべてが完璧なわけではありません。これらはRPCインターフェースの開発時に考慮しなければならない問題であり、見落とされがちな問題でもあります。
リクエストの繰り返し:プロバイダーの実行が終了した可能性がありますが、ネットワークジッターコンシューマーはタイムアウトしたと考えているため、この場合、再試行メカニズムによりリクエストが繰り返され、ダーティデータの問題が発生するため、サーバーはインターフェースのべき等を考慮する必要があります。
コンシューマーの負荷容量を減らす:プロバイダーが一時的なジッタではないが、実際にパフォーマンスの問題がある場合、複数回再試行しても成功しませんが、コンシューマーの平均応答時間が長くなります。たとえば、通常の状況では、プロバイダーの平均応答時間は1秒であり、コンシューマーはタイムアウト時間を1.5秒に設定し、再試行の数は2に設定されているため、1つの要求に3秒かかるため、コンシューマーの全体的な負荷は、これは高QPSサービスであり、連鎖反応を引き起こして雪崩を引き起こす可能性があります。
爆発的な再試行ストーム:コールリンクが4つのサービスを通過すると、最低のサービスDがタイムアウトし、上流のサービスが再試行を開始します(再試行時間が3に設定されていると想定した場合)。負荷が3倍、Cが9倍、Dが27倍の場合、サービスクラスタ全体が雪崩になる可能性があります。
適切なタイムアウト期間を設定するにはどうすればよいですか?
RPCフレームワークのタイムアウトの実装原理と考えられる副作用を理解した後、次の方法に従ってタイムアウトを設定できます。
呼び出し元のタイムアウトを設定する前に、まず、サービスに依存するTP99の応答時間を理解します(依存サービスのパフォーマンスが大きく変動する場合は、TP95も確認できます)呼び出し元のタイムアウトは、これに基づいて50%増やすことができます。
RPCフレームワークがマルチグラニュラータイムアウト設定をサポートしている場合、グローバルタイムアウト時間はインターフェースレベルでの最長時間にかかる時間よりわずかに長く、各インターフェースのタイムアウト時間はメソッドレベルでの最長時間にかかる時間および各メソッドのタイムアウトよりわずかに長くする必要があります。時間は、実際のメソッド実行時間よりも少し長くする必要があります
再試行可能なサービスと再試行不可能なサービスを区別するインターフェイスがべき等性を実装していない場合、再試行回数を設定することはできません。注:読み取りインターフェースは自然なべき等であり、書き込みインターフェースはビジネスドキュメントIDを使用するか、呼び出し元で一意のIDを生成してサーバーに渡すことができます。このIDは、重みを防ぎ、ダーティデータの導入を回避するために使用されます。
RPCフレームワークがサーバーのタイムアウト設定をサポートしている場合は、前の3つのルールに基づいて順番に設定されるため、クライアント設定なしで構成を回避でき、隠れた危険が軽減されます。
ビジネスの観点からは、サービスの可用性要件がそれほど高くない場合(内部アプリケーションシステムなど)、タイムアウトの再試行回数を設定せずに直接手動で再試行できます。メンテナンス後
再試行回数の設定が大きいほど、サービスの可用性が高くなり、ビジネスの損失をさらに減らすことができますが、パフォーマンスのリスクも大きくなります。これは、包括的に数回(通常は2回、最大3回)に設定する必要があります
呼び出し元が高QPSサービスである場合、サーバーが時間外の場合は、ダウングレードおよびヒューズ戦略を検討する必要があります。(たとえば、要求の10%以上が間違っている場合は、再試行メカニズムを停止して直接ブローし、他のサービス、非同期MQメカニズムを呼び出すように変更するか、呼び出し元のキャッシュデータを使用します)
最後に、簡単な要約:
RPCインターフェースのタイムアウト設定は単純なようですが、実際には大学の質問が非常に多くあります。多くの技術的な問題(インターフェイスのべき等、サービスの低下と融合、パフォーマンスの評価と最適化など)だけでなく、ビジネスの観点から必要性を評価する必要があります。それが何であるかを理解し、RPCインターフェースを開発するときに、この知識がよりグローバルな視点を与えることを願っています。
コードワードは簡単ではありません。この記事があなたにとって貴重であると思われる場合は、それを友達の輪に転送してクリックして読んでください。励ましとサポートをありがとう!
【終わり】
よりエキサイティングな推奨事項
ガートナーコンテナー製品で1位を獲得したAlibaba Cloudは、クラウドネイティブの重要な戦いに勝利しました!
Tencentのインタビュアーが私にこのバイナリツリーを尋ねました、私はたまたまです|フォースプログラム
☞マイクロソフトは一人のために会社を買収しましたか?ソニーのプログラムを解読し、ハッカーの小説を書き、彼の厳しいプログラムの人生を見てください!
機械学習プロジェクトテンプレート:MLプロジェクトの6つの基本ステップ
☞IBM、Microsoft、Apple、Google、Samsung ...ブロックチェーンにあるこれらの巨大テクノロジーはすでに多くのことを行っています!
上級プログラマーによるまとめ:Linuxプロセスを分析する6つの方法をすべてお話しします
今日の福祉:コメント欄にコメントを残すと、299元相当の「2020 AI開発者1万会議」の生放送のチケットを入手できます。指を動かして、言いたいことを書いてください。
クリックして元のテキストを読んでください。すばらしいです。
あなたが注文するすべての「ウォッチング」、私はそれを真剣に受け止めます