自由課題

学んだり、考えたり、試したりしたこと。

The Reactive Manifesto 日本語訳

まえがき

ガートナーのレポートInfoQの記事なんかをみると、リアクティブプログラミングというのがこれからくるよ、というのが書いてあったので少し前にちょっと調べていました。その時にアジャイルマニフェストみたいな感じでThe Reactive Manifestoという文書があったのでその時はザクッとみてふーんという感じだったのですが、最近見返そうと思ったらまだ日本語訳がありませんでした。

なので、もうちょっと詳しく読むついでに原文を書いた(一人である)Jonas Bonérさんに日本語訳を書いていいか聞いてみたら(今みたらこの時点で英語が若干間違えてる...)、

とわりとフランクに許可をもらったので訳してみました。
なお、訳が怪しい部分は原語をカッコで併記しています。訳の間違い・日本語のわかりにくさなどあるかも知れませんが、悪しからずご了承ください。

リアクティブ宣言(The Reactive Manifesto)

2013年9月23日発行

目次

  1. リアクティブに向かう必要性
  2. リアクティブアプリケーション
  3. イベント駆動
  4. スケーラブル
  5. レジリエント(障害耐性が高い)
  6. レスポンシブ(応答性が高い)
  7. 結論

宣言に署名する

リアクティブに向かう必要性

アプリケーションの要求は近年劇的に変化している。つい数年前には巨大なアプリケーションは10個単位のサーバ、秒単位の応答時間、数時間のオフラインメンテナンス、ギガバイト単位のデータを有していた。今日のアプリケーションは、モバイル端末から数千のマルチプロセッサが稼働するクラウドベースのクラスタまで全てに配置される。ユーザはミリ秒もしくはマイクロ秒単位の応答時間と、100%稼働していることを期待している。データに対する要求はペタバイト単位に拡大している。

初めはGoogleTwitterのような革新的なインターネット駆動の企業の領域で発生し、これらのアプリケーションの特性は大部分の業界で表面化している。金融や通信分野はこの新しい要求を満足するための新しいプラクティスを採用する第一人者であり、他の分野がこれに続いた。

新しい要求は新しい技術に対する需要を生み出す。以前のソリューションは管理されたサーバ群とコンテナを全面に打ち出していた。規模の変更(Scaling)は巨大なサーバ群を購入し、マルチスレッドによる並列処理によって獲得された。追加のサーバを付け加えることは複雑、非効率で高価なプロプライエタリなソリューションであった。

しかし、開発者に今日の需要を満たすアプリケーションを概念化・構築させるために現在新しいアーキテクチャが進化した。我々はこれらをリアクティブアプリケーションと呼ぶ。このアーキテクチャは開発者にイベント駆動スケーラブルレジリエントかつレスポンシブなシステムを構築することを可能にさせた: リアルタイム性のある応答性の高いユーザ体験、スケーラブルで障害耐性が高いアプリケーションスタックに裏打ちされ、マルチコアとクラウドコンピューティングアーキテクチャに配備される準備ができているものである。リアクティブ宣言はリアクティブに向かうために必要なこれらの重要な特性について記述する。

リアクティブアプリケーション

メリアム-ウェブスター[訳注: 辞書の名前]はリアクティブ(reactive)を"刺激に対して即座に反応しやすい"と定義しており、すなわちこのコンポーネントは"活動的"であり常にイベントを受信することができる。この定義はリアクティブアプリケーションの本質を捉えており、下記のようなシステムに焦点を当てている:

  • イベントに反応する: イベント駆動の性質は質を追求する
  • 負荷に反応する: 共有リソースに対する競合を回避することによりスケーラビリティに関心を向ける
  • 障害に反応する: すべてのレベルにおいて回復する能力を持つレジリエントなシステムを構築する
  • ユーザに反応する: 負荷にかかわらず優れた応答時間を保証する

これらのそれぞれはリアクティブアプリケーションに必要不可欠な特性である。これらの間には依存性があるため、これらの特性は標準的なレイヤ化されたアプリケーションアーキテクチャの感覚における層(tiers)のようなもので構成されるわけではない。代わりにこれらはすべての技術スタックを横断する設計上の特性として表現される。

図1 リアクティブな性質

以下で私たちはこの4つの性質のそれぞれについてより詳しく観察し、どのようにこれらが相互に関連しているかを見ていく。

イベント駆動

重要性について

非同期通信に基づいたアプリケーションは疎結合な設計を実装しており、純粋に同期関数をコールするアプリケーションより格段に望ましい。送信側と受信側はどのようにイベントが伝搬されるかという詳細に関わらず実装することができ、通信の内容に焦点を当てるインターフェイスを可能とする。これは拡張・進化、保守しやすい実装を導き、柔軟性と保守コストを削減することを可能とする。

非同期通信の受信側はメッセージを受信するかイベントが発生するまで休止状態を保てるため、イベント駆動のアプローチは既存のリソースの効率的な使用を可能とし、単一のハードウェアスレッドを共有する多数の受信者が存在することを可能とする。高負荷の状況下にあるノンブロッキングアプリケーションはこのことでブロッキング同期と通信の基本要素に基づく伝統的なアプリケーションよりも低い遅延高いスループットを有する。これはより低い運用コスト、利用率の増加とユーザの満足をもたらす。

鍵となるビルディングブロック

イベント駆動のアプリケーションにおいては、各コンポーネントイベント - ある事実に関する情報の独立した断片 - の生成と消費によって相互作用する。これらのイベントは非同期・ノンブロッキングの携帯で送信と受信が行われる。イベント駆動システムはプル(pull)ポーリング(poll)よりもプッシュ(push)を頼りにする傾向がある。言い換えると、アプリケーションは消費側が継続的にデータを問い合わせたり待ったりすることによってリソースを浪費するかわりに、準備ができたときにデータを消費側にプッシュするのである。

  • イベントの非同期的な送信 - メッセージ通信(message passing)とも呼ばれる - はアプリケーションが設計上高度に並行であることを意味し、変更なしにマルチコアのハードウェアの活用を行うことができる。CPU内のどのコアもどのメッセージイベントを処理することができ、並列化の機会の動的な増加を後押しする。
  • ノンブロッキングは、たとえ故障時や突発的な事態の下にあるときでも、常時アプリケーションが応答可能(responsive)であるために継続的な処理をさせるための能力を意味する。応答を行うために必要なリソース - 例えばCPU、メモリやネットワーク - は独占されてはならない。このようなことによってノンブロッキングは低遅延・高スループットを両立しつつよりよいスケーラビリティを可能とする。

伝統的なサーバサイドアーキテクチャは共有された変更可能な(mutable)状態と単一スレッド上でのブロッキング操作に基づいている。これら両者は需要の変化に対応するためにシステムをスケーリングするときに困難に直面する。変更可能な状態を共有することは同期を必要とし、これは付随的な複雑さと非決定性を持ち込み、プログラムコードを理解・維持することを困難にする。ブロッキングによってスレッドをスリープ状態にしておくことは有限のリソースを使い果たし、スレッド起床時のコストの上昇を招く。

イベント駆動のシステムはコンポーネントとサブシステムの間の疎結合を可能とする。このレベルの間接性は、私たちがこれから見ていくように、スケーラビリティと高い障害耐性のための必要条件の一つである。コンポーネント間の複雑さと密結合を排除することによって、イベント駆動のアプリケーションは既存のアプリケーションへの影響を最小限にしつつ拡張することができる。

アプリケーションが高性能や高いスケーラビリティのための要件により負荷が加わった場合、どこにボトルネックが発生するか予測するのは難しい。そのため、アプリケーション全体が非同期かつノンブロックであることが重要である。典型的な例では、これは設計はUI上(ブラウザ、RESTクライアントやその他)のユーザリクエストからリクエストのパース、Web層でのディスパッチング、ミドルウェア上のサービスコンポーネント、キャッシングや下位のデータベースに渡って設計がイベント駆動である必要があることを意味する。もしこれらの層の一つがイベント駆動でない場合、 - データベースに対するブロッキング呼び出しを行う、共有された変更可能な状態に依存する、時間のかかる同期操作を呼び出す - パイプラインのすべては停止し、ユーザは遅延の増大とスケーラビリティの減少に苦しむだろう。

アプリケーションは上位層から下位層の全てでリアクティブである必要がある。

鎖の中の最も弱い結合部を削除することの必要性はアムダールの法則によってわかりやすく図式化されており、Wikipediaによれば以下のように説明されている:

並列計算におけるマルチプロセッサを用いたプログラムの速度向上はプログラムの逐次処理部分により制限される。例えば、プログラムの95%が並列化可能である場合、並列計算を用いた速度向上の理論的最大値は図に示すように20倍であり、どれだけ多くのプロセッサを用いたとしても変わらない。

図2 アムダールの法則

スケーラブル

重要性について

スケーラブルという単語はメリアム-ウェブスターによると"需要に対して容易に拡張もしくはアップグレードする能力がある"として定義されている。スケーラブルなアプリケーションはその利用状況に応じて拡張されることができる。これはアプリケーションに弾力性(elasticity)を付与する、すなわち需要に応じてスケールインもしくはアウト(ノードを追加・削除する)する、ことによって獲得される。加えて、このアーキテクチャは再設計もしくはアプリケーションのリライトなしに容易にスケールアップもしくはダウン(あるノードに多くの、もしくは少数のCPUコアを割り当てる)することを可能とする。弾力性はクラウドコンピューティング環境においてアプリケーションを操作するコストを最小化することを可能とし、使った分だけ課金するモデル(pay-for-what-you-use model)からの恩恵を受けることができる。

スケーラビリティはリスクを管理する助けにもなる: ユーザ負荷に対してサービスを継続するのに少なすぎるハードウェアはユーザの不満と離脱を招き、特段の理由もなく多すぎるハードウェアと管理要員が待機していることは不要な支出をする結果を引き起こす。スケーラブルなソリューションは、アプリケーションが将来利用可能になるハードウェアを使い切れないままになるリスクを緩和する : 私たちは今後十年で千個単位ではないにしても百個単位のハードウェアスレッドを持つプロセッサを手に入れることになり、これらの潜在能力はアプリケーションに対して超細粒度でスケーラブルであることを要求することになるだろう。

鍵となるビルディングブロック

非同期メッセージに基づいたイベント駆動システムはスケーラビリティの基礎を提供する。コンポーネントとサブシステムの間の疎結合と位置からの独立性は同じプログラミングモデルとセマンティックを維持したままシステムを複数のノードにスケールアウトすることを可能とする。コンポーネントインスタンスの追加はイベントを処理するためのシステム能力を増強することができる。実装という意味では複数のコアを活用してスケールアップすることと、データセンターやクラスタ内のノードをさらに活用することによってスケールアウトすることの間には違いはない。アプリケーションのトポロジー(接続形態)は、設定とアプリケーションの使用状況に応じた適応的な実行時アルゴリズムの一方もしくは両方を通じて表現される運用上の決定である。私たちはこれを位置透過性(location tranparency)と呼ぶ。

ゴールは透過性のある分散コンピューティング、分散オブジェクトもしくはRPCスタイルの通信を実装しようとしているのではないことを理解することが重要である - これは以前試みられ失敗している。代わりに私たちは非同期メッセージ通信を通じてネットワークを包含することをプログラミングモデルに直接的に表現することを必要としている。真実のスケーラビリティは当然ながら分散コンピューティング、ネットワークを横断したノード間の通信を利用した私たちが本質的に予測不能(リンクは英語サイト)であるとして認識しているもの、に影響している。このため、ネットワークプログラミングの制約、トレードオフと障害シナリオを、事物をシンプルにしようとするための何らかの不完全な抽象化の背後に隠ぺいするのではなく、プログラミングモデル上明示的にすることが重要である。この結果として分散環境において発生する典型的な諸問題を解決するための共通のビルディングブロックをカプセル化したプログラミングツールを導入することが同様に重要である - これは合意を得る(achirving consensus)、もしくは高度の信頼性を提供するメッセージの抽象化を行う機構のようなものである。

レジリエント(障害耐性が高い)

重要性について

アプリケーションのダウンタイムはビジネスに起こりうる問題の中で最もダメージが大きいことがらの一つである。通常、この影響は動作が単純に停止することであり、収入の流れに穴を残すことになる。長期的に見ると、これは顧客も不幸にし、ビジネスをよりひどく傷つける評判の悪化を招く。アプリケーションの障害耐性の高さは、驚くべきことに無視されるかその場しのぎの処置で改良される要件となっている。これは荒すぎる粒度の手段を用いることによる不適切なレベルの粒度により対処されることを意味する。よく使われる技術としてサーバのクラスタリングがあり、実行時およびサーバの障害から復帰するために用いられる。不幸なことに、サーバのフェイルオーバは極めてコストがかかり、危険でもある - 潜在的にはクラスタ全てが壊滅する連鎖的な障害を引き起こす恐れがある。この理由は、これが本来コンポーネントレベルの細粒度における障害耐性の高さを用いて対処すべき障害管理の手法としては不適切なレベルの粒度であるからである。

メリアム-ウェブスターは障害耐性の高さ(resilience)を以下のように定義している:

  • 弾性を持って形状を維持するある実体もしくは物体の能力
  • 困難から素早く復帰する素質

リアクティブアプリケーションでは、障害耐性の高さは最初から設計する項目であって後から追加するものではない。プログラミングにおいて障害を最初に考えるべき構造とすることは、障害に対応・管理する方法を提供し、実行時に自己治癒を行うことにより障害に対して高度に耐性のあるアプリケーションを構築する手助けをする。伝統的な故障対応は局所的には守備的であり、大局的には積極的過ぎるため、これを獲得することはできない - 障害が発生した、もしくは全てのアプリケーションインスタンスのフェイルオーバを開始する正しい時と場所で例外(exceptions)に対処するべきである。

鍵となるビルディングブロック

障害を管理するために、私たちは他の問題のないコンポーネントに障害が伝搬しないように障害を隔離する手段が必要であり、加えて故障した状況の安全な外部から管理できるように障害を観察する手段が必要である。思い浮かぶパターンとして隔壁パターン(リンクは英語サイト)があり(下図を参照)、あるコンポーネントが故障したとしても他には影響を与えないためにシステムは複数の安全なコンポーネント群から構築される。これは連鎖的故障(リンクは英語サイト)の問題を防止して隔離した状態で問題の管理を可能とする。

隔壁

イベント駆動モデルは、スケーラビリティを確保できるものであり、障害管理の側面を達成するために必要な要素を有する。イベント駆動モデルにおける疎結合は、障害がその状況とともに補足でき、メッセージとしてカプセル化され、エラーを特定してどうやって対応すべきかを判断できる、完全に隔離されたコンポーネント群を提供する。

このアプローチはビジネスロジックがクリーンで、予期しない状況への対処から分離され、区画分けするために障害が明確にモデリングされ、観測され、管理され、宣言的な方法で設定され、システムが自己治療と自動復帰できるシステムを生成する。このシステムは区画が階層的に構築された場合にベストに動作する。これは対処できる力を持つレベルに達するまでエスカレーションする大企業のようである。

このモデルの美しさは、このモデルがリアクティブなコンポーネントと非同期イベントに基づく純粋なイベント駆動であり、そのため位置透過であることである。実際にこれは局所的な文脈としては同様な意味を持つ分散環境で動作することを意味する。

レスポンシブ(応答性が高い)

重要性について

応答性が高い(Responsive)はメリアム-ウェブスターで"適切な応答・反応が素早いこと"として定義されている。私たちはこの単語を一般的な意味で用いるので、CSSメディアクエリと徐々に進化をしていることでよく知られるレスポンシブウェブデザイン(Responsive Web Design)と混乱しないでほしい。

レスポンシブなアプリケーションはリアルタイムで魅力的、リッチで共同作業がしやすい(collaborative)。応答性が高くインタラクティブな体験を通じて顧客の居心地をよくさせることにより、ビジネスはオープンで継続した顧客とのやりとりを生み出す。このやりとりは顧客を、より効果的に、問題解決や仕事を完遂することに繋がり役に立つという感情にさせる。例としてユーザに同時かつリアルタイムにドキュメントを編集することを可能にするGoogle Docsがあり、これは顧客同士が複数で互いの編集内容を見たり、その場でコメントすることを可能とする。

イベントに反応するアプリケーションはタイミングよく反応することが必要であり、これは障害の発生時も同様である。もしアプリケーションが適切な一定時間内に反応できなかった場合 - これは遅延として知られる - 実質的には利用できないことになり、障害耐性が高いとはみなされない。

ハードリアルタイムシステム制約を満たす能力がないことは、軍事や医療制御システムのようないくつかのアプリケーションではシステム全体の障害とみなされる。全てのアプリケーションが厳密な要件を有しているわけではない。多数のアプリケーションは応答に対する制約を逸脱するにつれて有用さを減少させていく。例えば金融取引アプリケーションは適切な時間的応答性がないと現在の取引を失いかねない。

小売業界における商品閲覧・購入アプリケーションのような、もっと主流のアプリケーションは、応答時間の増大とともに有報さが減少していくことが計測されている。ユーザは応答性のよいアプリケーションを使うことにより、よりたくさんの買い物をする。

鍵となるビルディングブロック

リアクティブアプリケーションは観測可能な(observable)[訳注: 例えばObserverパターンを適用した]モデル、イベントストリームと状態を持ったクライアントを用いる。

観測可能なモデルは状態が変化した時に他のシステムがイベントを受信することを可能にする。このことはユーザとシステムの間のリアルタイムな接続を提供する。例えば、複数のユーザが同じモデルに対して同時に処理をした時に、顧客間で変更が双方向かつレスポンシブに同期され、そのことにより、まるでロックする制約がない状態で共有されているように見える。

イベントストリームは接続が確立されているということの基本的な抽象概念を形成する。このストリームをリアクティブに保つということは、ブロッキングを回避して代わりに非同期でノンブロックの変換と通信を可能にすることを意味する。

リアクティブアプリケーションはデザインパターンを適用して負荷に関わらずイベントの応答をO(1)もしくは最悪でもO(log n)の時間でおこなうことを保証することによりアルゴリズムのオーダの考え方を支持する。オーダの中に倍率は含まれるかも知れないが、顧客やセッション、商品や取引の数に制限されるわけではない。

アプリケーションには負荷状況に関わらず応答遅延を一定に保つために数々の戦略が適用される:

  • 突発的な通信状況においてリアクティブアプリケーションは、遅延を一定に保つための基本的なリソースへの知識や考慮をふまえたバッチ処理を適用することにより、IOや並列データ交換のような高価な操作を償還(amortize)する。
  • キューは状況に応じた適切な領域が設定される。つまり、キューの長さはリトルの法則を適用することによって所定の応答時間を満たすように設定される。
  • システムは適時適切なキャパシティプランニングとともにモニタリングされる。
  • サーキットブレーカ(リンクは英語サイト)が発動した時に即座に使用可能な切り替え処理の戦略により、障害は隔離される。

例として、リッチクライアント - ブラウザベースもしくはモバイルアプリ - でありよりよいユーザ体験を約束するウェブアプリケーションについて検討してみる。このアプリケーションはロジックと状態の保管をクライアント側で行い、クライアントにはデータが変更された時にUIを変更する機構を提供する観測可能なモデルが提供される。WebSocketもしくはServer-Sent Eventsのような技術がUIとプッシュ可能なイベントストリームと直接接続されることを可能にし、イベント駆動システムはバックエンドとクライアントの間を結ぶ。このことは、非同期・ノンブロックデータ転送を用いることにより、スケーラブルかつレジリエントな方法でブラウザやモバイルアプリに対してイベントをプッシュすることをリアクティブアプリケーションに可能とする。

このことを念頭におくと、イベント駆動スケーラブルレジリエント、そしてレスポンシブというこれら4つの性質が凝集した全体を形成するためにどう関連しているが見えてくる。

図4 リアクティブな特性

結論

リアクティブアプリケーションは、ソフトウェア開発における最近の幅広い課題ににバランスのとれたアプローチで対処している。イベント駆動でメッセージベースに構築することにより、スケーラビリティレジリエントさを保証するための方法を提供する。これらに立脚してでアプリケーションはリッチでレスポンシブなユーザ操作をサポートする。数々のシステムがここ数年で急速にこれらの設計方針に従うだろう。