banner
田野放空

田野放空

认真扮演一个擅长白日做梦的普通人

如何設計すると、堅牢なサービスを作ることができるか

最近辞職した後、面接と履歴書の準備中に、以前の仕事中に発生したサービスの問題を振り返りました。いくつかの事故では、ピーク時のトラフィックによっていくつかのサービスがダウンした可能性があります。これはデータベースの問題やメモリなど、さまざまな問題が原因です。その背後には、サービス自体が十分に堅牢でないことがあり、いくつかのノードがダウンした後、リトライストームが発生し、それによって雪崩が発生します。これらのサービスがメインプロセスのサービスである場合、システム全体の雪崩を引き起こす可能性があります。

image-20230401221427201

多くのサービスは、上記の図に示すように、負荷が最大限に達する前までは正常にサービスを提供できますが、トラフィックがサービスが処理できる最大限界を超えると、外部のサービス能力は急激に低下し、トラフィックが過負荷でなくても、トラフィックを完全にゼロにしても、サービス能力はなかなか回復しません。

時には、サービス能力が回復するのを待つのが困難であり、上位のトラフィックが戻ってきた後、サービスは瞬時に多くの人工的または自動的なリトライリクエストによってダウンすることがあります。

もし私たちのバックエンドサービスがこのように「もろい」場合、過負荷になるとすぐに倒れてしまい、なかなか立ち上がることができない場合、私たちの安定性保証の仕事はできません。大規模なアプリケーションでは、数千のサービスがあり、それぞれのサービスには多くのノードがあります。このような場合、すべてのノードの安定性を保証し、トラフィックによってサービスがダウンしないようにすることは非常に困難です。これは実現不可能です。予期しない局所的な異常は常に発生するからです。

したがって、私たちはアプリケーション全体を堅牢にする必要があります。各サービスは一定の堅牢性を持つ必要があります。個々のサービス自体(単一のマシンまたは分散システムであるかどうかに関係なく)に対して、この要件は非常に高くなく、実現するのは難しいことではありません。

image-20230401222233706

私たちは、「堅牢な」サービスを実現するために、負荷下でのパフォーマンスが上記の図に示すようになるようにする必要があります。負荷が増加すると、出力は線形に増加し、外部負荷がサービスの理論的な最大負荷を超えた後、一時的な揺れを経て、外部負荷がどれほど高くても、サービスは安定して外部に出力され、この出力は理論的な最大負荷に近い方が良いです。つまり、「堅牢な」サービスは、過負荷の状況でも安定した出力サービスを提供できるべきであり、処理能力を超えるリクエストは断固としてサービスを拒否するべきです。

  • 各サービスの各ノードは、自身の負荷をリアルタイムに表す指標を持つ必要があります。指標がサービス能力の上限を超える場合、サービス能力を超えるリクエストを拒否する必要があります。
  • サーバーサイドとクライアントサイド(ここでのクライアントは単にアプリではなく、このサービス自体のクライアントであり、通常はバックエンドの負荷状況を把握し、自身の戦略を調整するための統合された SDK である場合があります。別のサービス内の SDK である場合もありますし、アプリ内の SDK である場合もあります)の間にはフィードバックメカニズムが必要であり、クライアントはバックエンドの負荷状況を把握し、自身の戦略を調整できる必要があります。

一般的に言って、各サービスの各ノードには次のようなことが必要です:1. 自分が忙しいかどうか、限界に近づいているかを把握する必要があります。2. 忙しい場合はクライアントに伝える必要があります。クライアントがそれを知らないと、うっかりしてサービスをダウンさせてしまうかもしれません...

次に、これらの 2 つのポイントについて詳しく説明し、自分のサービスの負荷を表す指標を見つける方法と、過負荷時にクライアントと協議する方法について説明します。

サービスの負荷を表す指標#

サービスの負荷を表す指標とは、この指標がある閾値を超えると、サービスノードのサービス能力が急激に悪化し、長時間回復しないことを意味します。

私たちは、負荷テストを使用して、標準的なサーバー環境での適切な QPS、TPS などの値を見つけることができます。そして、制限流量、サーキットブレーカーなどのツールを使用してピーク値を制限することができます。ただし、実際の状況に応じて、制限のピーク値を一定の範囲内に設定することができます:

  1. サービスノードが水平にスケーリング可能な場合、非常に正確である必要はありません。サービスの負荷を CPU や他のリソースの限界まで追い込むことに過度にこだわる必要はありません。負荷テストの結果に基づいて、あまり攻撃的ではない値を設定することができます。ミックスデプロイやオーバーセールを使用して、サーバーリソースの利用率をさらに高めることができます。
  2. タイムアウト時間を考慮する必要があります。処理が完了しても、クライアントがタイムアウトしている可能性があるため、リクエストの返信は破棄されるだけです。
  3. いくつかのマシンが他の機能のサービスを実行する可能性があることを考慮する必要があります。たとえば、マシンの監視ツール、プライマリとセカンダリのノードの同期機能、ハートビート機能などです。したがって、閾値の設定では余裕を持たせる必要があります。より詳細なサービスでは、リクエストを優先度ごとに分類し、異なるカウンターを設定することも考えられます。
  4. このカウンターの閾値を自動的に調整する自己適応的なものにすることは可能ですか?つまり、サービスノードの内部リソース状況に応じて閾値のサイズを動的に調整することはできますか?正直なところ、ほとんどの場合、それは必要ありません。シンプルなほどエラーが少なくなります。ほとんどの場合、単一ノードのパフォーマンスにこだわる必要はありません。現在のマシンの爆発的な時代において、サービスの安定性はマシンよりも高価です。

クライアントとサーバーの協調#

正直なところ、私はクライアントについてはあまり詳しくありませんが、この問題は実際にはバックエンドがビジー状態でのリトライロジックの最適化です。

クライアントのリトライ戦略は非常に精巧なロジックであるべきですが、非常に見落とされやすいものです。一般的なリトライ戦略は、等間隔のリトライ、等比数列やフィボナッチ数列などのリトライ間隔を徐々に長くすることを考える人が多いです。

しかし、リトライは実際には 2 つの問題を解決するためのものです。1 つはネットワークのパケットロスや瞬断に対するフェイルオーバーであり、もう 1 つはサーバーノードの障害に対するフェイルオーバーです。前者に対しては、リトライ間隔はできるだけ短いほうが良いです。瞬時のリトライが最適です。しかし、後者に対しては、この間隔が短すぎると、リトライストームを引き起こし、バックエンドノードをさらに困難な状況に追い込む可能性があります。しかし、悲劇は、クライアントの視点からは、どの状況が発生しているのかを区別することができないことです。

したがって、最も簡単な解決策は、サーバーノードがビジー状態の場合、受信したリクエストを単純に破棄するのではなく、これらのリクエストに対してすべて RET_BUSY をクライアントに返すことです。クライアントが RET_BUSY を受け取ると、現在のリトライロジックから抜け出し、リトライの間隔を長くするようにする必要があります。さらに、サーバーサイドは自身の状況に応じて返り値をグレード分けすることができます。たとえば、RET_RETRY をクライアントに即座にリトライさせ、RET_BUSY や RET_VERY_BUSY をクライアントに異なるリトライサイクルに引き延ばすことができます。ここでは非常に多くの精巧なプレイがあり、さまざまな複雑な問題を解決することができます。皆さんは自分のエンジニアリングシナリオに合わせて実践してみてください。

ここまで、私たちは「堅牢な」サービスをどのように作るかについて説明しました。本質的には、非常にシンプルなエンジニアリング手法を使用して、自己のビジー度合いを初歩的に認識できるサーバーサイドと、サーバーのビジー度合いに応じて自動的にリトライできるクライアントを実装することです。これにより、外部のリクエストのプレッシャーがどれほど大きくても、制御可能な出力を提供できます。

実際のエンジニアリング実践では、サーバーノードは、平均 CPU 利用率が 70%以上、瞬時の CPU 利用率が 90%を超える状況で、持続的な安定した外部サービスを提供できるようにする必要があります。テスト環境では、限界状態での持続的な安定した出力を実現できるようにする必要があります。

上記の堅牢性を実現するために改造が必要なサービス#

上記のメカニズムは RPC フレームワークに組み込むことができますし、要求がそれほど高くないほとんどのサービスには十分です。

分散ストレージシステムでは、パフォーマンスの問題にはより高い要件があります。ストレージシステムの安定性に関するエンジニアリングの難しさは、最も複雑で基本的なものです。ストレージノードは単純な再起動では問題を解決できません。また、リソースの消費次元は複雑で、CPU 以外にもメモリ、ページキャッシュ、ネットワークスループットと中断、ディスクの IOPS とスループットなど、多くのリソースの考慮が必要です。さらに、流量の低下後、通常はストレージシステムは迅速にサービスを回復することはできません(例えば、マスタースレーブ同期を待つ必要がある、マイナーやメジャーコンパクションを待つ必要がある、ディスクフラッシュを待つ必要があるなど)。そのため、オンラインストレージシステムの堅牢性には、通常以上の要素を考慮する必要があります。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。