リアクティブシステムが注目を集める理由
リアクティブシステムとは何か?
ここ最近、「リアクティブ」という言葉を耳にしたり、SNS上で目にしたりという機会が増えたのではないでしょうか? リアクティブは、目まぐるしく進化するソフトウェア構築技術の中で、次世代ソフトウェア構築のためのベースとなる技術の一つとして注目されています。しかし、ひとくちにリアクティブといっても、単にリアクティブと言ってみたり、リアクティブプログラミング、React.js、リアクティブストリームなど様々で、リアクティブの明確な定義が存在せず、その捉え方もまた様々です。そこで本連載では「リアクティブシステム」にフォーカスして、「リアクティブシステム」とは何なのか、どうやって作るのか、「リアクティブシステム」によって誰がどんなふうに幸せになれるのかを紹介していきます。
様々なリアクティブ
「リアクティブ(Reactive)」という単語自体は「反応的な」という意味を持ちます。「リアクティブシステム」の話に入る前に、様々な「リアクティブ」について、各公式ページやWikipediaの内容を中心に紹介しておきます。
Reactive Programming(リアクティブプログラミング)
データの流れ(data flow)とデータの値の変化の伝播にフォーカスしたプログラミングパラダイム。使用されるプログラミング言語で静的/動的なデータフローが簡単に表現でき、実行モデルは自動的にデータフローを通じて変更を伝播する。
Functional Reactive Programming(ファンクショナルリアクティブプログラミング)
関数型プログラミングの構成要素(例えばmap、reduce、filter)を用いたリアクティブプログラミングのためのプログラミングパラダイム。明示的に時間をモデル化することによって、これらの問題を単純化することを目指し、プログラムのグラフィカルユーザーインターフェース(GUI)、ロボット工学、音楽に使用されている。
Reactive Extentions(リアクティブエクステンション)
観測可能(observable)なコレクションとLINQスタイルのクエリ演算子を使用して、非同期なイベントベースのプログラムを合成するライブラリ。(Rx = Observable + LINQ + Scheduler)
- 非同期データストリームをObservableで表現
- 非同期データストリームに対するクエリにLINQ演算子を使用
- 非同期ストリームの並行性をSchedulerでパラメータ化
Reactive Streams(リアクティブストリーム)
ノンブロッキングなバックプレッシャーを備えた非同期ストリーム処理の標準を提供するための取り組みで、ランタイム環境(JVMとJavaScript)、ネットワークプロトコルの取り組みを含む。
React.js
FacebookとInstagramが開発した、ユーザーインターフェースを作るためのJavaScriptライブラリ。多くはMVCのVとしてReactを選択され、時間の経過とともに変化するデータをもつ大きなアプリケーション構築のために作成された。
リアクティブ宣言
リアクティブシステムとは、ひとことで言うと「即応性(Responsive)、耐障害性(Resilient)、弾力性(Elastic)、メッセージ駆動(Message Driven)の4点を備えたシステム」です(図1)。
どのようなシステムかイメージできますでしょうか? なんとなく「レスポンスが高速で、障害に強くって、負荷に応じてスケールするシステムのことかな」というくらいは想像がつくのではないでしょうか。
上述のリアクティブシステムの定義と図1は、リアクティブ宣言(Reactive Manifesto)に書かれている説明を引用したものです。このリアクティブ宣言とともに、リアクティブシステムが何かを解説していきます。リアクティブ宣言の原著は「The Reactive Manifesto」で、現在の最新バージョンであるv2.0は2014年9月16日に策定され、現在(2015年10月時点)10,000を超える署名を得ています。
「The Reactive Manifesto」はTypesafe社の創設者の1人であり、CTOであるJonas Boner氏(@jboner)らが中心となって策定したもので、昨今のアプリケーションへの要求の変化に応える事ができるのは「即応性(Resiponsive)」、「耐障害性(Resilient)」、「弾力性(Elastic)」と「メッセージ駆動(Message Driven)」を備えたシステムであり、これを「リアクティブシステム」と呼ぼう、と宣言されています。
変化するアプリケーションへの要求
リアクティブ宣言に書かれているアプリケーションへの要求の変化とは、次のようなことです。
要求の種類 | これまでの要求 | 現在の要求 |
---|---|---|
サーバ構成 | 数十台のサーバ | モバイル機器から数千のマルチコアプロセッサによって動作する クラウドベースのクラスタまであらゆる機器上に配備 |
応答時間の単位 | 秒 | ミリ秒 |
オフラインメンテナンス許容時間 | 数時間 | 0時間 |
扱うデータの単位 | ギガバイト | ペタバイト |
ソフトウェアの実行環境や構築環境が進化し続ける現在または近い将来、ユーザーからは「50台のサーバで動かしていたけど、クラウド上で1,000コアの環境を作りたいんだ」や「レスポンスを得るのに3秒かかるけど、500ミリ秒くらいにしたいのよ。それと、年1回の2時間のメンテナンスなんとかならないかしら」、さらには、「うちのデータ最初は100ギガくらいだったけど、そろそろペタ行くんじゃないかな」といった声が聞こえてきそうです。
1,000ものコアをフル活用するアプリケーションをどうやって作るのか? 3秒ルールに四苦八苦していたアプリケーションが、ミリ秒で応答することが出来るのか? 稼働率を限りなく100%に近づけるにはどうすればよいのか? これらの問題を解決するのがリアクティブシステムの設計原則であり、これらをシステム構築のたびに検討するのではなく、最初からこの原則に沿って構築すべきだと主張するのがリアクティブ宣言です。
リアクティブシステムを支える4つの要素「即応性(Resiponsive)」、「耐障害性(Resilient)」、「弾力性(Elastic)」と「メッセージ駆動(Message Driven)」には矢印(図2)に示されるような関係性があります。これらをわかりやすくするために「最終的に届けたい価値」、「支える原理」、「手段」といった3つの層に分類すると次のように表現できます。また、理解を助けるため、本連載では、以降、Responsiveは「高レスポンス」、Elasticは「伸縮性」と表現します。
リアクティブシステムが最終的に届けたい価値は「高レスポンス(Responsive)」、つまり、ユーザーに可能なかぎり速く応答することです。高レスポンスは、いかなるときも維持されなければなりません。通常の負荷状況の時だけでなく、ワークロードが変動しても高レスポンスを維持し続けるために伸縮性(Elastic)が必要です。あるいは障害に直面したとしても、システム全体をダウンさせることなく、高レスポンスを保ち続けるために耐障害性(Resilient)が必要なのです。図2の矢印が示すように、高レスポンスを支える原理が「伸縮性」と「耐障害性」であり、これを実現する手段が、メッセージ駆動(Message Driven)のアーキテクチャです。
リアクティブ宣言が主張する4つの要素の関係をご理解いただけましたでしょうか? では、それぞれの要素をもう少し詳しく見ていきたいと思います。
高レスポンス(Responsive)
高レスポンス(Responsive)は、最終的にリアクティブシステムがユーザーに届けたい価値で、可能な限り速くレスポンスすることを指します。高レスポンスを維持するには、流れるデータの大きさを知り、正しくリソースプランニングを行い、コンポーネントの分割や分散を考慮しなければなりません。そして、これらはユーザーに対するレスポンスだけでなく、システムに問題が発生した際の対処も迅速に行われる必要があります。当然のことながら、平常時のみ高レスポンスが提供できればよいわけではなく、どんな場面でも維持する必要があります。それを実現するために不可欠になるのが、「伸縮性」と「耐障害性」です。
伸縮性(Elastic)
システムが通常の負荷状況の時だけでなく、一時的な高負荷状況でも高いレスポンスを維持するには、変動するワークロードに応じて割り当てるリソースを増減させることで、応答時間を一定水準に保つ伸縮性が必要です。変動するワークロードに応じてスケールアウト・スケールインするためには、負荷状況を常に監視しその変化に応じて処理を分散させる必要があります。また、各コンポーネントは独立して動作し、1つのコンポーネントがボトルネックを持たないようにすべきです。
耐障害性(Resilient)
高レスポンスは、部分的な障害が発生した時もなお維持し続けなければなりません。部分的な障害が発生してもシステム全体を危険にさらすことなく回復させ、高いレスポンスを維持するのがリアクティブシステムです。耐障害性を高めるためには、故障したコンポーネントを修復したり、代替となるコンポーネントを再接続するメカニズムが必要になります。
「耐障害性」という言葉を聞いて「フォールトトレラント(Fault tolerant)」を思い浮かべる方も多いと思います。フォールトトレラントという言葉は日本語(カタカナ)として定着していますが、こちらも「障害に強い」というような意味を持ちます。では、何が違うのでしょうか?「レジリエント(Resilient)」という単語は、次のような意味を持ちます。
- 元の形状に跳ね戻る物質や物体の能力
- 困難からすぐに回復する力
障害が起こらないようにしたり、単に障害に耐えるのではなく、障害が起こったあとに元の状態に戻ったり、回復する力ということです。
まず、レジリエントなシステムを構築するために最初に理解しておくべきことは、「障害は必ず起こる」ということです。もちろん、障害は起こらないに越したことはありません。しかし、障害の種類はアプリケーションに潜在する不具合から、ハードウェアの問題、ヒューマンエラーなど、小さいものから大きなものまで様々で、100%防ぐことは不可能と言ってよいでしょう。しかし、ユーザーはそうは考えてくれません。コンシューマー向けのサービスやシステムであった場合、その障害によりシステムがダウンしてしまうと、よほど独自性のあるサービスでない限り、ユーザーはあっという間に去っていきます。再び戻ってきてもらうためには、膨大な時間と努力を要することでしょう。エンタープライズシステムでも、企業の信頼を失うことは簡単です。このため、障害が起こるか、起こらないかではなく、いつどこで起こるかを知り、それに備え回復させることが重要となります。
例えば、飛行機のエンジンが故障した時のために予備のエンジンを1台積んだとします。そうすることで、飛行中に1つのエンジンが故障したとしても予備のエンジンで飛行を続けられ、フォールトトレラントであると言えます。しかし、エンジンが1つ故障した時点で、これ以上の故障には耐えられない状態となってしまい、2つ目のエンジンが故障すれば、飛行機は墜落してしまいます。これはフォールトトレラントですが、レジリエント(Resilient)ではありません。レジリエントは「お互いが隔離され、お互いを守りあう」という状態でなければいけません。障害が起こっても、お互いを守り合い再び回復できるのが、リアクティブシステムの耐障害性(Resilient)なのです。
メッセージ駆動(Message Driven)
高レスポンスを支える伸縮性と耐障害性を実現するアーキテクチャがメッセージ駆動です。関数を呼び出しその応答を待って次の関数を呼び出すのでなく、メッセージ駆動はコンポーネント間のやり取りを非同期なメッセージ送信で行います。メッセージを送信したコンポーネントは、その結果を待つことなく次の仕事にとりかかり、受信側のコンポーネントは自らのメッセージボックスに届いたメッセージに反応し、ひたすら自分の仕事をこなします。そうすることで、コンポーネント間の関係が疎結合になるだけでなく、隔離性(isolation)や位置透過性(location transparency)を保証します。
コンポーネント間の関係が疎結合になることで、別のコンポーネントに影響を与えることなく実装できることや、コンポーネントのテスタビリティが向上し独立して確認することができるなど、ソフトウェアの維持コストを確実に削減できます。また、ハードウェアリソースを効率的に活用できることもメリットの一つです。現代のハードウェアは単一コアの能力を上げるのではなく、多くのコアを並べて利用すること(メニーコア)が主流となっています。しかしこれまでのように、動作するアプリケーションのアルゴリズムが逐次的に実行しなければならない状態では、メニーコアを有効に活用できません。メニーコアを手に入れた今、システムを高速化する鍵はソフトウェアのアルゴリズムが握っているということはアムダールの法則からもわかります。
アムダールの法則(Amdahl's Law)によれば、プログラムの中で並列実行可能な部分(Parallel Portion)の割合が少ないと、ソフトウェアの高速化(図3のY軸:Speedup)のためにプロセッサの数(図3のX軸:Number of Processors)を増やしても、その効果が制限されることがわかっています。
逐次的に実行しなければならない部分が、時間にして全体の50%であれば、ハードウェアにお金をつぎ込んで大量にプロセッサを追加しても、2倍より速くなることはありません(図3の水色の線)。例えば、全体で10分かかる処理のうち、50%にあたる5分を逐次的に実行しなければならない場合、いくらプロセッサを追加したところで5分より短くなることはないということです。逐次的に実行しなければならない部分が5%、つまり95%が並列実行可能な実装であれば、最大で20倍まで速くなります(図3の緑色の線)。さらに、100%並列実行可能であれば、プロセッサを増やせば増やすほどスピードアップすることになります。高レスポンスなシステムを構築するにためには、より並列実行可能なソフトウェアのアルゴリズムがいかに重要であるかをおわかりいただけるでしょう。そして、リアクティブシステムでこれを可能にするのが、メッセージ駆動のアーキテクチャなのです。
まとめ
いかがでしたでしょうか? リアクティブシステムの話を聞いて、「これはスゴイ」「こんなのは今までになかった」と感じられましたでしょうか? いや、そうでもないという方も多いのではないでしょうか。それもそのはずで、高レスポンス(Responsive)や伸縮性(Elastic)、さらに、アムダールの法則(Amdahl's Law)においても、個々に捉えると決して新しいトピックではありません。リアクティブ宣言では誰もが必要としているこのようなシステムを、リアクティブシステムと呼ぼうと宣言されたものであるためです。しかし、何の武器も持たずにこのようなシステムを構築することは決して容易ではありません。
そこで次回は、JVM上で動くリアクティブシステムを作るためのプラットフォーム「Typesafe Reactive Platform」を紹介します。「Typesafe Reactive Platform」は、リアクティブシステムにおけるリーディングカンパニーであるTypesafe社が提供するプラットフォームです。「高レスポンス」「伸縮性」「耐障害性」「メッセージ駆動」を備えたシステムの構築をどう実現するのかを紹介したいと思います。
参考資料:
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Lightbend Reactive Platformによるリアクティブシステムの構築
- CAのProFit-Xに見るリアクティブシステム事例
- リアクティブシステムを実現するツールキットAkka その2
- Akka Clusterで超レジリエンスを手に入れる
- ESBの成り立ち
- コンテナ上のマイクロサービスの認証強化 ~StrimziとKeycloak~
- ここまでできる!標準ファイルサービス
- Elasticsearchを開発するElastic、最新バージョン5.0とElastic Stackを解説
- Akka Streamsで実装するリアクティブストリーム
- Red Hatが提供するJBoss Enterprise Middlewareとは