第8章 耐障害性と信頼性

本章では、JBoss Enterprise Service Bus の信頼性に関する特徴を説明します。ここでは、本リリースの耐障害性に確認いただけます。また、アプリケーションの耐障害性を向上する方法に対するアドバイスも記載しています。しかし、進める前に重要な用語をまず定義していきます。
ディペンダビリティ とは、配信するサービス (ユーザーによって認識される動作) に正当な信頼が置けるなど、コンポーネントの信用性を意味します。コンポーネントの信頼性は、継続した適切なサービス配信を示す基準となります。システムが提供するサービスが仕様に適合しなくなると障害が発生します。エラーは障害の原因となるシステム状態の一部で、不良はエラーの原因となります。
耐障害性 システムとは、コンポーネントの障害が発生しても特定の目的を達成するよう設計されたシステムです。通常、耐障害性を提供するための技術は、整合状態回復のメカニズムや故障したコンポーネントにより生成されるエラーを検出するメカニズムを必要とします。レプリケーションやトランザクションなど、複数の耐障害性の技術が存在します。

8.1. 障害の分類

システム上で稼働しているアプリケーションが適切であるか検証する前に、システムの動作を正式に記述する必要があります。これにより、アプリケーションの動作制限を確立し、この制限を緩和または強化する意味を明確にします。
耐障害性に関して正式な記述を行うメソッドとして、発生すると考えられる障害の種類によってシステムコンポーネントを分類するメソッドを推奨します。
各コンポーネントには特定の入力セットに対するそのコンポーネントの正しい動作を記す関連詳細があります。正常に機能するコンポーネントはこの詳細に一致する出力を生成します。障害のあるコンポーネントからの応答は、必ずしもそこまではっきり限定されているわけではありません。特定の入力に対する特定のコンポーネントからの応答は、指定の値が正しく、指定の制限時間内に生成されていれば、正しいとみなされます。
起こりうる障害を 4 つに分類すると、脱落、値、タイミング、任意になります。
脱落の不良/障害
コンポーネントが別のコンポーネントからの入力に応答せず、予期される出力を生成しない場合、脱落の不良とそれに関連する脱落の障害が発生していることになります。メッセージを紛失することのある通信リンクなどが、脱落の不良が発生しているコンポーネントの例になります。
値の不良/障害
正しい時間間隔内にコンポーネントが応答しても、値が正しくないような不良を値の不良と呼びます (これに関連する障害は値の障害と呼ばれます)。破損したメッセージを時間通りに配信する通信リンクには値の不良が発生しています。
タイミングの不良/障害
タイミングの不良は、コンポーネントが正しい値で応答しても指定された間隔で応答しない (早すぎるか遅すぎる) 原因となります。この不良に関連する障害はタイミングの障害です。正しい値を生成するのに応答に過度の遅延があるオーバーロードしたプロセッサーにはタイミングの障害が発生しています。タイミングの障害は、計算を時間的に制約するシステム内でのみ発生します。
任意の不良/障害
前述の障害クラスは、その値または時間ドメインでコンポーネントがどのように失敗するかを指定するものでした。前述の障害クラスが適応されない方法でコンポーネントが両方のドメインで失敗する可能性もあります。このような出力を生成する障害コンポーネントは任意の障害 (ビザンチン障害) が発生していると言えます。
任意の不良はコンポーネントの指定動作に対するあらゆる違反の原因となります。 他の不良タイプはすべて特定タイプの不良動作については除外します。脱落の不良タイプはもっとも限定的となります。したがって、不良分類の分布帯で言えば脱落と任意の不良はこの分布帯の両端となり、他の不良タイプがその中間に位置することになります。このため、後ろの方の障害分類はそれより前に位置する障害分類の特徴を包含することになります。つまり、脱落不良 (障害) は値またはタイミングの不良 (障害)の特殊なケースとして扱うことができます。こうした順序は以下のように階層で表すことができます。
不良分類の階層

図8.1 不良分類の階層

8.1.1. JBossESB と不良モデル

JBossESB 内では、すべてが任意の障害によって影響を受けます。ご想像の通り、任意の障害を検出するのは本質的に大変困難です。任意の障害に対する耐性をシステムに持たせるプロトコルは存在しますが、何段階もの調整やデジタル署名が必要になる場合がほとんどです。JBossESB の今後のリリースでは、こうした方法の一部がサポートされる見込みです。
値、タイミング、脱落の障害にはアプリケーションに関するセマンティック情報が必要となることが多いため、JBossESB が直接このような障害タイプに対応できることは限られています。しかし、メッセージヘッダー内で RelatesTo や MessageID などの JBossESB の機能を正しく使用すると、アプリケーションによって、受信したメッセージが受信待ちのメッセージであるか遅延のメッセージであるかを判断することができます。一方向要求に対する非同期の一方向応答など、サービスによる提供が早すぎたメッセージは、基礎のトランスポート実装が原因で損失する可能性があります。たとえば、HTTP などのプロトコルを使用する場合、応答がアプリケーションに渡されるまで応答を保持できる有限のバッファーがあります (オペレーティングシステムレベルで設定される)。このバッファーの容量を越えると、新しいメッセージを保持するためバッファ内の情報が失われることがあります。この制限によって FTP や SQL などのトランスポートが必ずしも影響を受けるとは限りませんが、同様の動作を生じるような他のリソース制限を受ける可能性があります。
遅延のメッセージに対して耐性を持たせる方が早着のメッセージに対して耐性を持たせるより簡単な場合があります。しかし、アプリケーションの観点では、早着のメッセージが失われると (バッファーのオーバーフローなどにより)、無限に遅れるメッセージとの区別がつかなくなります。そのため、メッセージ損失の際に再試行のメカニズムを使用するようアプリケーション (コンシューマーとサービス) を構築する場合、コンシューマーが順序を無視して早い応答を受け取って不適切に処理するという例外 (これにより値の障害が発生します) にてタイミングと脱落の障害に対応できるようにします。メッセージヘッダー内で RelatesTo と MessageID を使用すると、ペイロード全体を処理しなくても不適切なメッセージシーケンスを見つけることができます (別のオプションを使用することもできます)。
同期された要求と応答の対話パターン内では、応答が一定の時間内に受信されないと RPC で構築されたシステムの多くは自動的に要求を再送信します。しかし、現在の JBossESB は自動的に再送信を行わないため、Couriers または ServiceInvoker 内でタイムアウトのメカニズムを使用し、いつメッセージを再送信するか(または再送信する必要があるか)を判断しなければなりません。高度なトピックの章にある通り、メッセージの配信に影響するようなサービスの障害発生が疑われる場合、そのメッセージを再送信します。

注記

メッセージをサービスに再送信する場合は注意してください。現在、JBossESB には保持される結果やサービス内の再送の検出といった概念はないため、重複するメッセージもサービスへ自動的に配信されます。そのため、サービスが同じメッセージを複数回受け取る可能性があります (損失したのが最初の要求ではなく最初のサービス応答だった場合など)。このように、サービスは同じ作業を実行しようとすることがあります。再送信を使用する場合 (明示的または ServiceInvoker のフェールオーバーメカニズムを使用)、必ず冪等になるようサービス内で複数の要求を処理する必要があります。
トランザクションの使用 (JBossTS で提供されるものなど) やレプリケーションプロトコル (JGroups のようなシステムで提供される) はこうした障害モデルの多くに対して耐障害性を持たせるのに役立ちます。さらに、障害が原因で先に進めない状態の場合、 トランザクションを使用するとアプリケーションのロールバックが可能になり、 基礎となるトランザクションシステムはまるでその作業がまったく試行されなかったかのように表示してデータの整合性を保証します。現在の JBossESB は JBoss Application Server 内にデプロイされると JBossTS を通じてトランザクション的なサポートを提供します。

8.1.2. 障害検出機能と障害推測機能

理想的な障害検出機能とは、分散システム内でエンティティ (プロセスやマシンなど) の活発さをはっきりと判定できる機能のことです。しかし、障害が発生したシステムと応答が遅いシステムを区別することは不可能なため、一定時間内に障害の検出を保証するのは不可能です。
現在の障害検出機能は、エンティティが使用できるかを判断するためにタイムアウト値を使用します。たとえば、マシンが指定時間内に「are-you-alive?」というメッセージに応答しない場合、障害が発生したと想定します。こうしたタイムアウトに割り当てられる値が正しくない場合 (ネットワークの混雑などが原因で)、正しくない障害が想定され、一部のマシンが別のマシンの障害を「検出」するのに他のマシンは検出しないなど矛盾を招く可能性があります。したがって、ネットワークの混雑やマシンの負荷が最悪な場合など、使用される分散環境内で想定できる最悪の事態に対して、通常こうしたタイムアウトが割り当てられます。しかし、分散システムやアプリケーションが実行の度に予期した通りに動作することはまずありません。したがって、想定する最悪の事態が変化することも考えられる上、障害検出に対して間違った判断をする可能性は常にあります。
障害の検出を保証することは不可能ですが、既知のアクティブなエンティティは相互に通信することができるため、通信できないエンティティは障害が発生していると合意することができます。これが障害推測機能の動作になります。あるエンティティが別のエンティティに障害が発生していると予測した場合、残りのエンティティー間でプロトコルが実行され、障害発生の合意を問います。エンティティの障害発生に合意した場合、障害が発生していると見られるエンティティはシステムから除外され、その後このエンティティによる動作は許可されません。1 つのエンティティが障害の発生を予測しても、すべてのエンティティが同じような判断をするとは限りません。障害が発生していないエンティティがシステムから除外された場合は、別のプロトコルを実行して活動状態であることを認識させなければなりません。
障害推測機能の長所は、分散環境内で正しく機能しているすべてのエンティティが、障害の発生が疑われる別のエンティティの状態について合意することです。短所は、障害推測のプロトコルは重く、一般的に複数回の合意が必要となることです。また、タイムアウト値に基づいて障害が推測されるため、障害のないエンティティが除外されることもあり、(重大となる可能性のある) リソースの利用や可用性が低下します。
障害検出のメカニズムが時折、誤った答えを返す可能性があるという事実に耐性を持つことができるアプリケーションもあります。ただし、これ以外の他のアプリケーションの場合、エンティティが機能しているとの誤った判定はデータ破損などの問題を招くおそれがあり、ミッションクリティカルなアプリケーション (航空機制御システムや原子炉監視など) の場合などは人命に関わる結果となる可能性があります。
現在の JBossESB は障害検出または障害推測をサポートしていません。この短所については今後のリリースで対応していきたいと考えています。現在のところ、前述のような特定のサービスに障害が発生しているのかどうかを判定試行する技術 (MessageID およびタイムアウト/再試行) を使用するコンシューマー側とサービスは利用側で開発して頂く必要があります。アプリケーションに障害の検出から疑わしい障害の処理まで行わせた方がより効率的な場合もあります。

8.2. 信頼性の保証

見てきたように、分散システム内で障害が発生する恐れのある状況は多くあります。このセクションでは障害がどのように JBossESB およびそれにデプロイされたアプリケーションに影響を及ぼすのかについて具体的な例を説明していきます。推奨のセクションでは、こうした障害によりよい耐性を持たせるための JBossESB 設定方法やアプリケーション開発を前提とした設定方法について見ていきます。
JBossESB 内には多くのコンポーネントやサービスがあります。障害発生時に依存するアプリケーションの一部またはすべてに対して認識できない可能性がある障害がいくつかあります。たとえば、コンシューマー側がサービスが機能するために必要な EPR 情報をすべて完全に取得してしまった後にレジストリサービスがクラッシュすると、アプリケーションに不都合な影響はありません。しかし、これより前に障害が発生すると、アプリケーションは先に進めなくなります。したがって、いずれの信頼性保証の判定においても、障害が発生した時期また障害の種類を考慮する必要があります。
信頼性や耐障害性を 100 % 保証することは不可能です。ハードウェアの障害や人的ミスをなくすことはできません。しかし、システムが障害に耐えられる可能性を高くしたり、データの整合性を維持して向上することは可能です。トランザクションやレプリケーションなどの耐障害性技術はパフォーマンスに影響します。アプリケーションの知識に基づいてパフォーマンスと耐障害性の妥協点を見つけるのが最良の方法です。特定の方法をすべてのアプリケーションに対して一様に使用すると、その方法が必要でない状況下ではパフォーマンスの劣化につながります。そのため、JBossESB によってサポートされる耐障害性技術の多くはデフォルトで無効になっています。耐障害性技術は必要に応じて有効にするようにしてください。

8.2.1. メッセージの損失

メッセージの紛失や遅延がどのようにアプリケーションに対して悪影響を与える可能性があるのかについては既に見てきました。また、JBossESB 内でどのようにメッセージがなくなってしまうのかについても例をいくつか見てきました。このセクションではメッセージ紛失についてもう少し詳しく見ていくことにします。
多くの分散システムがポイントツーポイント (1 コンシューマー側と 1 プロバイダー) またはグループベース (複数のコンシューマー側と 1 プロバイダ) で信頼できるメッセージ配信をサポートしています。一般的に信頼性に課されるセマンティックとは、たとえ障害が存在していてもメッセージが配信されるまたはメッセージが受信者に届かなかったことを確実に送信者が知ることができるということになります。信頼できるメッセージング実装を採用しているシステムは受信者にメッセージが配信されるのとそのメッセージが受信者によって処理されるのとは区別する場合が多く、たとえば、単にサービスにメッセージが届くというのと、サービスがメッセージの内容を処理する時間を確保する前にクラッシュが続けて発生した状況とは区別されます。
メッセージの配信や処理で前述の障害セマンティックを提供するトランスポートで、JBossESB 内で使用できるのは JMS のみです。トランザクション処理されるセッションでは (JMSEpr のオプション部分)、障害が存在していてもメッセージの受信や処理を保証することが可能です。サービスによる処理中に障害が発生した場合、メッセージが後で再処理されるよう JMS キューに戻されます。しかし、トランザクション処理されるセッションはトランザクション処理されないセッションより大幅に遅くなることがあるため、使用には注意が必要です。
JBossESB によってサポートされる他のトランスポートは、トランザクションや確実な配信についての保証がないため、メッセージが損失する可能性はあります。しかし、ほとんどの場合でメッセージ損失の可能性は低くなります。送信側と受信側の両方で同時に障害が発生しない限り (このような障害が発生することはまずありませんが、起こり得ます)、送信側は JBossESB によってメッセージ配信の障害に関する通知を受けます。処理中に受信側に障害が発生し、応答が予期されていた場合、受信側は最終的にタイムアウトするため再試行できます。

注記

非同期メッセージの配信を使用すると障害の検出/推測が難しくなる可能性があります (理論的には不可)。 アプリケーションを開発する際はこの点を考慮にいれてください。
こうした理由から、高度なトピックの章に説明がある再配信プロトコルとメッセージフェールオーバーが最良のアプローチであると言えます。サービスの障害を予測すると、代替の EPR (1 つが利用可能と仮定) を選択して使用します。しかし、予測した障害が発生していなかった場合は、複数のサービスが同じメッセージで同時に動作する可能性があります。そのため、フェールオーバーにはロバストなアプローチですが、使用する場合は注意が必要です。このアプローチは、同じメッセージを複数回実行することと 1 回実行することが同じ場合など、サービスがステートレスで冪等である場合に最適なアプローチです。
多くのサービスおよびアプリケーションに対してこのタイプの再配信メカニズムは適切に動作します。単一の ERP 全体にわたり提供される堅固さは大きな利点となるでしょう。クライアントとサービスが失敗するまたはサービスに誤って障害が発生したとみなされるなど、動作しない状況の障害モードはかなり珍しいことになります。サービスをべき等にできない場合は、JBossESB がメッセージのトランザクション的配信か維持される結果のなんらかの形式をサポートするまで、 JMS を使用するかサービスが再送信を検出して同じ作業を同時に実行している複数のサービスに対処できるコードを使用してください。

8.2.2. エンドポイントの障害を疑う

これまで、障害の検出や予測を判断する難しさについて説明してきました。実際、クラッシュしたマシンと非常に動作の遅いマシンを区別するのは、障害が発生したマシンが回復するまで不可能です。ネットワークを分割して効果的に複数の個別のネットワークとして動作させるため、ネットワークをパーティション化することができます。しかし、ネットワークが分割されると、ネットワークの異なる部分に存在するコンシューマはその部分で利用可能なサービスしか見えなくなります。このような状態は「スプリットブレインシンドローム」とも呼ばれます。

8.2.3. サポート対象となるクラッシュ障害モード

JBossESB は、トランザクションまたは JMS などの、信頼性の高いメッセージ配信プロトコルを使用している場合、システム全体がシャットダウンするという突発故障からも回復できます。
トランザクションや確実なメッセージ配信プロトコルを使用しない場合、関係するエンドポイントの可用性が保証される場合のみ JBossESB は障害に対応できます。

8.2.4. コンポーネント固有

本セクションでは JBossESB 内の特定のコンポーネントおよびサービスについて説明します。

8.2.4.1. ゲートウェイ

ゲートウェイでメッセージが受け取られると、信頼できないトランスポートを使って ESB 内に送信しない限りメッセージが失われることはありません。JMS、FTP、SQL などの JBossESB トランスポートはすべてメッセージを確実に配信するまたはシステムから絶対に削除されないようにするよう設定することができます。残念ながら HTTP はこのようには設定できません。

8.2.4.2. ServiceInvoker

ServiceInvoker は非同期で送信された不達のメッセージを再配信キューに配置します。メッセージの同期配信に失敗すると、送信側へ直ちに通知されます。ServiceInvoker が正しく機能するためには、トランスポートによって配信の失敗を送信側に明確に知らせなければなりません。送信側と受信側で同時に障害が発生すると、メッセージが損失することがあります。

8.2.4.3. JMS ブローカー

JMS ブローカーに配信できないメッセージは再配信キューに置かれます。エンタープライズデプロイメントにはクラスタ化された JMS ブローカーが推奨されます。

8.2.4.4. アクションパイプライン

多くの分散システムと同様、サービスが存在する場所内にあるコンテナが受信するメッセージと、最終的な送り先で処理されるメッセージを区別します。メッセージが正しく配信されても、アクションパイプライン内での処理中にエラーやクラッシュが発生するとメッセージを損失することがあります。前述の通り、受信したメッセージが処理中に削除されないように JBossESB トランスポートの一部を設定し、エラーやクラッシュが発生してもメッセージを損失しないようにすることができます。

8.3. 推奨

上記で説明してきたような障害モデルの概要とそれら障害に対する JBossESB 内の耐性機能を考慮し、次のようなことが推奨されます。
  • ステートレスで羃等なサービスを開発するようにしてください。不可能な場合は、アプリケーションが再送信の試行を検出できるように、MessageID を使用してメッセージを特定するようにしてください。メッセージ送信の再試行を行う場合は、同じ MessageID を使用してください。冪等でなく、再送信された メッセージを受信すると同じ処理を再度行うようなサービスは、なるべくトランザクションを使用して MessageID に対してステート移行を記録するようにします。ステートレスサービスを基にするアプリケーションの方がスケーラビリティが高い傾向にあります。
  • ステートフルなサービスを開発する場合はトランザクションと JMS 実装を使用してください (できればクラスター化する)。
  • レジストリのクラスタ化を行い、 クラスタ化された耐障害性のあるバックエンドデータベースを使用して単一障害点がないようにします。
  • メッセージストアが必ず高可用性のデータベースで支えられているようにします。
  • 他のサービスやオペレーションと比較して、 より高い信頼性や耐障害性の機能を必要とするサービスおよびサービス上のオペレーションを明確に識別しておきます。 これによりこれらのサービスで JMS 以外のトランスポートを対象とすることができるようになり、 アプリケーション全体のパフォーマンスを向上させることができる場合があります。 JBossESB により別々の EPR を通じて同時にサービスが使用されるようにすることが可能なため、 こうした異なるサービスの特性 (QoS) をアプリケーション固有の必要条件に応じて異なるコンシューマー側に提供することも可能になります。
  • ネットワークのパーティションはサービスが失敗しているかのように表示する可能性があるため、 クラッシュしたように誤って識別されることに対処できないサービスに対してはこの種の障害が起きる傾向にあるトランスポートの使用は避けてください。
  • 場合によっては (HTTP など)、サーバーがメッセージを処理した後、応答する前にサーバーがクラッシュすると、別のサーバーが同じ処理を行う原因となります。これは、サービスが受信したメッセージを処理した後にマシンで障害が発生した場合と、サービスが受信したメッセージを処理する前に障害が発生した場合を区別できないからです。
  • 非同期 (一方向) の配信パターンを使用するとサービスの障害検出が困難になります。これは、要求への応答が任意の時間に到達できる場合、一般的に損失メッセージや遅延メッセージの概念がないからです。応答が全くない場合は障害の検出がより難しくなるため、アプリケーションセマンティックに依存してメッセージの不達を判断しなければならない場合もあります (例:銀行口座の残高が予期した金額と異なる)。ServiceInvoker または Couriers を使用して非同期のメッセージを配信する場合、各操作 (deliverAsync など) から返答があってもメッセージがサービスによって動作したとは限りません。
  • メッセージストアは再配信プロトコルによって使用されますが、前述の通り、このプロトコルはロバスト性の向上に最善のプロトコルであり、トランザクションや確実なメッセージ配信を使用しません。そのため、障害によってはメッセージが全て損失したり (クラッシュの前にメッセージがストアに書き込まれない)、メッセージが複数回配信される原因となることがあります (再配信のメカニズムがストアからメッセージを取り出して無事配信するが、クラッシュによってメッセージがストアから削除されず、クラッシュから回復した後にメッセージが再度配信される)。
  • FTP など一部のトランスポートは、処理済みのメッセージを保持するよう設定することができますが、未処理のメッセージと区別するため独自にマークされます。通常、デフォルトでは処理されたメッセージは削除されますが、障害から回復した際に、アプリケーションが処理されたメッセージを判別できるよう、デフォルトを変更することもできます。
本章の障害に関する説明と反して、障害は頻繁に発生するわけではありません。年月をかけてハードウェアの信頼性は飛躍的に向上され、公式の検証ツールの使用を含め活発なソフトウェア開発が行われることによりソフトウェア関連の問題も少なくなってきました。サービスやアプリケーションを開発、デプロイしていく上で適切な方法を判断するのに役立つよう本章では障害についての説明をしています。パフォーマンスを低減させるような高いレベルでの信頼性や耐障害性が必ずしもすべてに必要なわけではありませんが、間違いなく必要とするものもあるからです。

このページには機械翻訳が使用されている場合があります (詳細はこちら)。