Reactive Programming
@maruyama097 丸山不二夫
In a recent post on the NetBeans developer site, one of the core maintainer observed that a single class had been patched over 14 times to fix threading related problems. --- Brian Goetz et al “Java Concurrency In Practice”
1/3 of the code in Adobe’s desktop applications is devoted to event handling logic 1/2 of the bugs reported during a product cycle exist in this code --- quoted in Martin Odersky’s “Deprecating the Observer Pattern”
Merriam-Webster defines reactive as “readily responsive to a stimulus”, i.e. its components are “active” and always ready to receive events. This definition captures the essence of reactive applications, focusing on systems that: event-driven, scalable, resilient, responsive ---
“Reactive Manifesto”
はじめに o Reactive プログラミングの歴史は、インタラク ティブなUIの登場とともに古い。表計算ソフトは 、もっとも身近な最も成功したReactive プログ ラミングの一つである。 o 現代のReactive プログラミングをドライブして いる力の一つは、ResponsiveなUIへの欲求 である。その主要な舞台は、Web UIである。 Web Componentとともに、post HTML5の 中心的なWeb技術の一つである、Model Driven View(MDV)は、こうしたReactive プ ログラミング技術と見ることが出来る。
はじめに o 現代のReactive プログラミングをドライブして いるもう一つの力は、非同期でイベント・ドリブ ンな処理への志向である。この方向は、CPUの メニコア化と分散処理の巨大なクラウドへの拡 大という、まったく異なる二つの分野でともに強 力に押し進められている。 o 2010年頃に公開されたマイクロソフト社の Rx(Reactive Extention)は、Observable の役割に注目し、Reactive プログラミングの 新しい手法を切り開いた画期的なものであった。
はじめに o Rxは、オープンソース化され、多くの言語に移 植され、また多くのReactive プログラミング言 語に大きな影響を与えた。 o Rxの応用で注目すべき事例は、Netflixによる RxのJavaへの移植と、それに基づくAPIの書 き換え・システムの再構築の成功である。 o それはまた、小さな関数から大きな関数を合成 するという関数型のアプローチの、大規模な実 システムでの有効性を示すものでもあった。
Agenda Part I o WebとReactiveプログラミング n n n n n n
非同期とイベント・ドリブンへの志向 Web UIとReactiveプログラミング Meteor AngularJS Dart Polymer.dart
Agenda Part II o Reactive Extensionの基礎 n n n n n n
LINQとEnumerable RxとObservable Observableを使ったEvent処理 Rx for JavaScript Observableを使った非同期処理 AndroidとRx
Agenda Part III o Observableを活用する n Observableのストリームを処理する n Observableを変形・加工する合成可能な 関数 n ServerサイドでのRxの事例 Netflix n NetflixでのObservableの利用
Agenda Part IV o Reactive Manifesto n The Need to Go Reactive
n n n n n n
Reactive Applications Event-driven Scalable Resilient Responsive Cocclusion
Part I WebとReactiveプログラミング
表計算とReactive Web UI
表計算ソフトはReactive 最も身近で、成功したアプリの一つ
表計算のアルゴリズムを考える Loop { セルCijへの入力/修正のイベント検出 Loop { セルCijへの入力/修正は終了したか? } セルCijに関連付けられている全てのマクロMnの検索 Loop { 新しいセルCijの値で全てのMnを再計算 }
}
Mnが定義されているセルCklの値を再表示
AngularJSの処理
AngularJSの処理 1. 2.
3. 4. 5. 6. 7.
‘X’ keyが押されると、ブラウザーはinputコントロールにkeydow nイベントを発する。 input directiveは、inputの値の変化を捉えて、Angular 実行コ ンテキスト内のアプリケーション・モデルを変更する為に、 $apply(“name = ‘X’;”) を呼び出す。 Angularは、 name = ‘X’; をモデルに適用する。 $digest ループが始まる。 $watch リストは、name属性の変化を検出すると、{{name}} 補完に通知をして、それが、つづいてDOMを更新する。 Angularは実行コンテキストを抜け出す。それで、keydownイベン トは終わり、それとともにJavaScriptの実行コンテキストも終わる。 ブラウザーは、更新されたテキストで、ビューを再描画する。
非同期とイベント・ドリブンへの注目
2009年11月 JSConf
Ryan Dahlの登場 o ここでは、node.jsの爆発的な拡大の起点となった、 2009年ベルリンで行われた、JSConfでのnode.j sの創始者Ryan Dahlの講演の一部を見てみよう。
“Node.js, Evented I/O for V8 Javascript” by Ryan Dahl
n http://jsconf.eu/2009/speaker/ speakers_selected.html#entry-3356 n http://s3.amazonaws.com/four.livejournal/ 20091117/jsconf.pdf
I/O は、違ったやり方で 行われる必要がある。 o 多くのWebアプリは、次のようなコードを使う。 var result = db.query("select * from T"); // use result o データベースを検索している間、ソフトウェアは 何をしているのだろう? たいていの場合、ただ レスポンスを待っているだけである。 o それに加えて、IOによる遅延は、コンピュータ の能力と比較すると、次のように、巨大なもの である。 http://s3.amazonaws.com/four.livejournal/20091117/jsconf.pdf
var result = db.query("select..");
o このようなコードは、全体のプロセスをブロック するか、複数のプロセスの実行をスタックさせる ことになるかのいずれかである。 o しかし、次のようなコードでは、プログラムは、すぐ にevent loopに戻ることが可能である。どのよ うなメカニズムも必要としない。 db.query("select..", function (result) { // use result });
o これが、I/Oが行われるべき方法である。 o では、何故、誰もがevent loopやCallbackや Non-BlokingI/Oを使わないのだろうか?
文化的偏見 o 我々は、I/Oを次のように行うと教えられてきた puts("Enter your name: "); var name = gets(); puts("Name: " + name);
o 我々は、入力を要求されたら、それが終わるま で何もしないと教えられてきた。次のようなコー ドは、複雑すぎるとして、退けられてきた。 puts("Enter your name: "); gets(function (name) { puts("Name: " + name); });
インフラの不在 o それでは、何故、誰もが event loopを使わな いのだろう? o シングル・スレッドのevent loopは、I/Oが nonblockingであることを要求するのだが、ほとん どのライブラリーはそうなっていない。 n POSIX async file I/O は使えない. n Man pagesは、関数がディスクにアクセスすること があることを触れていない。 (e.g getpwuid()) n Cに、クロージャーや無名関数がないことが、コール バックを難しくしている。 n データベースのライブラリーは(e.g. libmysql)、非 同期のサポートをしていない。
“Latency as an Effect” “Principles of Reactive Programming” Coursera lecture by Erik Meijer https://class.coursera.org/reactive-001/ lecture/51
2013 Nov 4th “Principles of Reactive Programming”
https://www.coursera.org/course/reactive
trait Socket { def readFromMemory(): Array[Byte] def sendToEurope(packet: Array[Byte]): Array[Byte] } val socket = Socket() val packet = socket.readFromMemory() val confirmation = socket.sendToEurope(packet)
PCでの処理時間
シーケンシャルな実行時間 val socket = Socket() val packet = socket.readFromMemory() // 50,000 ナノ秒ブロックする // 例外が発生しない時には、次の処理へ val confirmation = socket.sendToEurope(packet) // 150,000,000 ナノ秒ブロックする // 例外が発生しない時には、次の処理へ
1 ナノ秒を 1 秒だと思うと
1ナノ秒を1秒だとした場合の実行時間 val socket = Socket() val packet = socket.readFromMemory() // 3日間ブロックする // 例外が発生しない時には、次の処理へ val confirmation = socket.sendToEurope(packet) // 5年間ブロックする // 例外が発生しない時には、次の処理へ
Callback Hell o “A guide to writing asynchronous javascript programs” http://callbackhell.com/
Asynchronous javascript, or javascript that uses callbacks, is hard to get right intuitively.
o “Callback hell in nodejs?”
http://stackoverflow.com/questions/18095107/callbackhell-in-nodejs
In below code am I in callbackhell? How to overcome such scenario without using any async modules in pure javascript?
Web UIとReactiveプログラミング
Web UI での Reactive / Model Driven View(MDV)への志向 o Meteor.js n Reactive プログラミング n Reactive-contextとReactive-data-source n publish/subscribe モデル
o AngularJS n Imperativeからdeclarative n Data binding
o Polymer.js n Model Driven View
o .....
Polymer.dart Observables and Data Binding with Web UI https://www.dartlang.org/web-ui/ observables/
Overview of data binding in Web UI o Web UI helps you efficiently bind application data to HTML, and vice versa, with observables and observers. o Observables are variables, fields, or collections that can be observed for changes. o Observers are functions that run when an observable changes.
Efficiently tracking changes o Instead of asking every possible observable “Did you change?” on every event loop over and over, Web UI has an efficient mechanism to notify only the right observers at the right time.
Observing variables
Hello Web UI Web UI is {{ superlative }}
Change
library hello_world; import 'package:web_ui/web_ui.dart'; @observable String superlative = 'awesome'; const List
alternatives = const ['wicked cool', 'sweet', 'fantastic', 'wonderful']; int _alternativeCount = 0; String get nextAlternative => alternatives[_alternativeCount++ % alternatives.length]; changeIt() { superlative = nextAlternative; } main() { }
Observing classes Hello {{person.name}}!
Change Name
@observable class Person { String name; Person(this.name); } final Person person = new Person('Bob'); const List names = const ['Sally', 'Alice', 'Steph']; newName() { person.name = nextName; }
Observing collections final List timestamps = toObservable([]); void addTimestamp() { timestamps.add(new DateTime.now()); } void clear() { timestamps.clear(); }
Observing nested objects @observable class Person { String name; Address address; } @observable class Address { String city; } @observable Person person;
main() { person = new Person() ..name = 'Clark Kent' ..address = ( new Address() ..city = 'Metropolis' ); }
Expression Observers The time is
Update
@observable String msg; updateMsg() { msg = new DateTime.now().toString(); } main() { observe(() => msg, (_) { query('#msg').text = msg; }); }
Part II Reactive Extensionの基礎
Reactive Extension (Rx) by Microsoft
http://msdn.microsoft.com/en-us/ data/gg577609.aspx
Reactive Extensionとは? o “The Reactive Extensions (Rx) is a library to compose asynchronous and event-based programs using observable collections and LINQ-style query operators.” o 「Reactive Extensionは、非同期かつイベント ・ベースのプログラムをオブザーバブル・コレク ションとLINQスタイルの検索操作を利用して 構成する為のライブラリーである。」
講演資料 o 2010年 11月 DevCamp Keynote “Rx: Curing your asynchronous programming blues”
http://channel9.msdn.com/Blogs/codefest/ DC2010T0100-Keynote-Rx-curing-your-asynchronousprogramming-blues
o 2012年 6月 TechEd Europe “Rx: Curing your asynchronous programming blues” http://channel9.msdn.com/Events/TechEd/Europe/2012/ DEV413
Rx オープンソース化と移植 o 2012年11月 MS Open Tech からオープンソ ースとして公開。 Rx.NET :https://github.com/Reactive-Extensions/Rx.NET o あわせて、JavaScript、C++、Python、Ruby への移植版もオープンソースとして公開。 n n n n
RxJS : https://github.com/Reactive-Extensions/RxJS RxCpp :https://github.com/Reactive-Extensions/RxCpp RxPy : https://github.com/Reactive-Extensions/RxPy Rx.rb : https://github.com/Reactive-Extensions/Rx.rb
RxJava o Netflixは、Rx.NETをJavaに移植し、オープン ソースとして公開した。 RxJava: https://github.com/Netflix/RxJava/ o Netflixは、自社のシステムをRxJavaを用いて 書き換え、大規模システムでのReactive プロ グラミングの有効性を示した。 o 2013年2月 “Functional Reactive in the Netflix API with RxJava” http://techblog.netflix.com/2013/02/rxjavanetflix-api.html
RxJavaの移植によって、JVM上で走る、Clojure, Scala, Groovy, Jruby等の言語でもReactive Extension が走るようになった。
LINQとEnumerable データソースの抽象化として、.NET 3.0か ら導入されたLINQで、中心的な役割を果た すのは、Enumerableインターフェースで ある。 http://code.msdn.microsoft.com/101-LINQSamples-3fb9811b
LINQの最も重要なインターフェース
Enumerable
全てのCollectionクラスは、IEnumerableを実装している interface IEnumerable { IEnumerator Enumerator(); } interface IEnumerator : IDisposable { bool Next(); T Current { get; } void Reset(); }
LINQで利用出来るオペレータ Projection: Select, SelectMany Restriction: Where Partitioning: Take, Skip, TakeWhile, ... Ordering: OrderBy, OrderByDecsending, ... Grouping: GroupBy Set: Distinct, Union, Intersect, .. Conversion: ToList, ToArray, .. Generation: Range, Repeat Aggregate: Count, Sum, Min, Max, Average, ....
LINQは、SQLライクな検索構文で、データソースから データソースを導出することが出来る public void Linq1() { int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; var lowNums = from n in numbers where n < 5 select n; Console.WriteLine("Numbers < 5:"); foreach (var x in lowNums) { Console.WriteLine(x); } }
LINQでは、検索式の中にラムダ式を利用出来る public void Linq5() { string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; var shortDigits = digits.Where( (digit, index) => digit.Length < index);
}
Console.WriteLine("Short digits:"); foreach (var d in shortDigits) { Console.WriteLine( "The word {0} is shorter than its value.", d); }
SelectMany (多段のSelect) public void Linq14() { int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 }; int[] numbersB = { 1, 3, 5, 7, 8 }; var pairs = from a in numbersA from b in numbersB where a < b select new { a, b }; Console.WriteLine("Pairs where a < b:"); foreach (var pair in pairs) { Console.WriteLine( "{0} is less than {1}", pair.a, pair.b); } }
SelectMany (多段のSelect) public void Linq15() { List customers = GetCustomerList(); var orders = from c in customers from o in c.Orders where o.Total < 500.00M select new { c.CustomerID, o.OrderID, o.Total }; ObjectDumper.Write(orders); }
SelectMany (多段のSelect) public void Linq18() { List customers = GetCustomerList(); DateTime cutoffDate = new DateTime(1997, 1, 1); var orders = from c in customers where c.Region == "WA" from o in c.Orders where o.OrderDate >= cutoffDate select new { c.CustomerID, o.OrderID }; ObjectDumper.Write(orders); }
public void Linq41() { string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese" }; var wordGroups = from w in words group w by w[0] into g select new { FirstLetter = g.Key, Words = g };
}
foreach (var g in wordGroups) { Console.WriteLine( "Words that start with the letter '{0}':", g.FirstLetter); foreach (var w in g.Words) Words that start with the letter 'b': blueberry { banana Console.WriteLine(w); Words that start with the letter 'c': chimpanzee } cheese } Words that start with the letter 'a': abacus apple
Reactive Extensionと Observable Rxで中心的な役割を果たすのは、イベント のソースを抽象化したObservableである。 それは、データソースの抽象化として、LIN Qで中心的な役割を果たすEnumerableと よく似ている。
ObservableとObserver o Observableは、Enumerableと同様に、デー タの集まり。それは相互に変換可能。 o Observableには時間の概念が入っている。 それは時間とともにデータを生み出す。イベント の抽象に用いられる時に便利である。 o データ・ソースとしてのObservableが生成す るデータは、ObservableにObserverが Subscribeした時に、はじめて利用出来る。 o データはObservableからSubscribeの時に 指定したObserver上の関数に自動的に送り出 される。(Push)
Observer上の三つの関数
OnNext, OnError, OnComplete o ObservableからObserverへデータがpush される時に、次の三つの関数のみが利用される。 n OnNext(): 通常のデータの受け渡し。 n OnError(): エラーが発生したこと伝える。 n OnComplete(): 送るべきデータが終了したこと を伝える。
o これらの関数の定義は、Observableへの Subscribeの際に与えられねばならない。 o ObservableへのSubscribeは、Subscriptio nを返す。この値は、Sunscribeをやめる時に 利用される。
EnumarableとObservable Enumerable
どちらもデータの集りである
Observable
時間の流れ
EnumerableとObservableは、 相互に変換出来る Enumerable à Observable .ToObservable();
Observable à Enumerable .ToEnumerable()
Rxの最も重要なインターフェース Observable interface IObservable { IDisposable Subscribe(IObserver observer); } interface IObserver { void OnNext(T value); void OnError(Exception ex); void OnCompleted(); }
Observerが 実装する3つの タイプのメソッド
ObservableからObserverへの通知 OnNext (42)
OnNext (43)
OnNext (“Hello”)
OnCompleted
OnError (error)
OnNext* (OnError | OnCompleted)?
// Range()は、次のようなObservablを生成する // -1-2-3-4-5-6-7-8-9-10-| IObservable source = Observable.Range(1, 10); // ここでは、Subscribe()は、三つの引数を取っている。 // 順番に、OnNext,OnError,OnCompleteの定義である IDisposable subscription = source.Subscribe( x => Console.WriteLine("OnNext: {0}", x), ex => Console.WriteLine("OnError: {0}", ex.Message), () => Console.WriteLine("OnCompleted") );
Obser verが 実装する 3つの タイプの メソッド
Console.WriteLine("Press ENTER to unsubscribe..."); Console.ReadLine(); // Subscribeをやめる subscription.Dispose();
internal class Generate_Simple { private static void Main() { var observable = Observable.Generate(1, // 初期値 -1-2-3-4-5-| x => x < 6, // 条件 x => x + 1, // 増分 x => x, // 返り値 x=>TimeSpan.FromSeconds(1)).Timestamp(); // ここでは、SubscribeにはOnNextの定義だけが与えられている using (observable.Subscribe(x => Console.WriteLine( "{0}, {1}", x.Value, x.Timestamp))) { Console.WriteLine("Press any key to unsubscribe"); Console.ReadKey(); }
} }
Console.WriteLine("Press any key to exit"); Console.ReadKey();
class Select_Simple { static void Main() { // 一秒間隔で数列を生成する var oneNumberPerSecond = Observable. Interval(TimeSpan.FromSeconds(1)); var numbersTimesTwo = // 新しいObservableの生成 from n in oneNumberPerSecond select n * 2; // LINQが使える!
}
}
Console.WriteLine("Numbers * 2:"); numbersTimesTwo.Subscribe(num => { Console.WriteLine(num); } // OnNextの定義が引数で与えられている ); Console.ReadKey();
MoveNex t
IEnumerable IEnumerator
Environment
Have next!
IObservable IObserver
Reactive
Got next?
OnNext
Interactive
Application
Observableを使ったEvent処理
マウスイベントをObservableにする
マウスは、イベントのデータベース! var lbl = new Label(); var frm = new Form { Controls = { lbl } }; var moves = Observable. FromEvent(frm, "MouseMove"); using (moves.Subscribe (evt => { lbl.Text = evt.EventArgs.Location.ToString(); })) { Application.Run(frm); }
var txt = new TextBox(); var frm = new Form { Controls = { txt } }; var moves = Observable.FromEvent (frm, "MouseMove"); var input = Observable.FromEvent (txt, "TextChanged"); var movesSubscription = moves.Subscribe( evt => { Console.WriteLine("Mouse at: " + evt.EventArgs.Location); }); var inputSubscription = input.Subscribe( evt => { Console.WriteLine("User wrote: " + ((TextBox)evt.Sender).Text); }); using (new CompositeDisposable( movesSubscription, inputSubscription)) { Application.Run(frm); }
var moves = from evt in Observable. FromEvent(frm, "MouseMove") select evt.EventArgs.Location; var input = from evt in Observable. FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text; var movesSubscription = moves.Subscribe( pos => Console.WriteLine("Mouse at: " + pos)); var inputSubscription = input.Subscribe( inp => Console.WriteLine("User wrote: " + inp));
var overFirstBisector = from pos in moves where pos.X == pos.Y select pos; var movesSubscription = overFirstBisector.Subscribe( pos => Console.WriteLine("Mouse at: " + pos));
違った値が来た時のみデータを送る var input = (from evt in Observable.FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text) .DistinctUntilChanged(); Doは、Observablを変化させずに、OnNextを実行する var input = (from evt in Observable.FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text) .Do(inp => Console.WriteLine("Before DistinctUntilChanged: " + inp)) .DistinctUntilChanged(); Throttleは指定された時間の間、Observableのイベントを抑制する TimeSpan.FromSecond(1)は、一秒間の指定 var input = (from evt in Observable.FromEvent(txt, "TextChanged") select ((TextBox)evt.Sender).Text) .Throttle(TimeSpan.FromSeconds(1)) .DistinctUntilChanged();
using (input.ObserveOn(lbl).Subscribe(inp => lbl.Text = inp)) Application.Run(frm);
Reactive Extension for JavaScript
var source = null; // Observableとしてsourceを生成するコード(省略) var subscription = source.Subscribe( // 三つの関数の定義が順番に引数に与えられている function (next) { $("
").html("OnNext: " + next) .appendTo("#content"); }, function (exn) { $("
").html("OnError: " + exn) .appendTo("#content"); }, function () { $("
").html("OnCompleted") .appendTo("#content"); } );
var source = Rx.Observable.Empty(); OnCompleted var source = Rx.Observable.Throw("Oops!"); OnError: Oops var source = Rx.Observable.Return(42); OnNext: 42 OnCompleted var source = Rx.Observable.Range(5,3); OnNext: 5 OnNext: 6 OnNext: 7 OnCompleted
var source = Rx.Observable.Generate( 0, function (i) { return i < 4; }, function (i) { return i + 1; }, // Like a for loop function (i) { return i * i; } ); OnNext: 0 OnNext: 1 OnNext: 4 OnNext: 9 OnCompleted
RxJS: DOMイベントをObservableに
$(document).ready(function () { $(document).mousemove(function (event) { // A position tracking mechanism, // updating a paragraph called “content” $("
") .text("X: " + event.pageX + " Y: " + event.pageY) .appendTo("#content"); }); jQuery });
RxJS $(document).ready(function () { $(document).toObservable("mousemove") .Subscribe(function (event) { // A position tracking mechanism, // updating a paragraph called “content” $("
") .text("X: " + event.pageX + " Y: " + event.pageY) .appendTo("#content"); }); });
$(document).ready(function () { $(document).toObservable("mousemove") .Subscribe(function (event) { $("
") .text("X: " + event.pageX + " Y: " + event.pageY) .appendTo("#content"); }); $("#textbox").toObservable("keyup") .Subscribe(function (event) { $("
") .text("User wrote: " + $(event.target).val()) .appendTo("#content"); }); });
var moves = $(document).toObservable("mousemove") .Select(function(event) { return { pageX : event.pageX, pageY : event.pageY }; }); var inputs = $(“#textbox”).toObservable(”keyup”) .Select(function(event) { return $(event.target).val(); });
var movesSubscription = moves.Subscribe( function (pos) { $("
”) .text("X: " + pos.pageX + " Y: " + pos.pageY) .appendTo("#content"); }); var inputSubscription = input.Subscribe( function (text) { $("
") .text("User wrote: " +text) .appendTo("#content"); });
var overFirstBisector = moves .Where(function(pos) { return pos.pageX === pos.pageY; }); var movesSubscription = overFirstBisector .Subscribe(function (pos) { $("
") .text("Mouse at: "+pos.pageX+","+pos.pageY) .appendTo("#content"); });
var inputs = $(“#textbox”).toObservable(”keyup”) .Select(function(event) { return $(event.target).val(); } .DistinctUntilChanged(); ); var inputSubscription = input.Subscribe( function (text) { $("
") .text("User wrote: " +text) .appendTo("#content"); });
var inputs = $(“#textbox”).toObservable(”keyup”) .Select(function(event) { return $(event.target).val(); } .Throttle(1000) .DistinctUntilChanged(); ); var inputSubscription = input.Subscribe( function (text) { $("
") .text("User wrote: " +text) .appendTo("#content"); });
Functional Reactive Programming on Android With RxJava http://mttkay.github.io/blog/ 2013/08/25/functional-reactiveprogramming-on-android-withrxjava/
現在のAsyncTaskを使った処理 class DownloadTask extends AsyncTask { protected File doInBackground(String... args) { final String url = args[0]; try { byte[] fileContent = downloadFile(url); File file = writeToFile(fileContent); return file; } catch (Exception e) { // ??? } } protected void onPostExecute(File file) { Context context = getContext(); // ??? Toast.makeText(context, "Downloaded: " + file.getAbsolutePath(), Toast.LENGTH_SHORT) .show(); } }
private Observable downloadFileObservable() { return Observable.create(new OnSubscribeFunc() { @Override public Subscription onSubscribe( Observer fileObserver) {
try { byte[] fileContent = downloadFile(); File file = writeToFile(fileContent); fileObserver.onNext(file); fileObserver.onCompleted(); } catch (Exception e) { fileObserver.onError(e); }
}
} });
return Subscriptions.empty();
class MyFragment extends Fragment implements Observer { private Subscription subscription; @Override
protected void onCreate( Bundle savedInstanceState) { subscription = AndroidObservables .fromFragment(this,downloadFileObservable()) .subscribeOn(Schedulers.newThread()) .subscribe(this); } private Observable downloadFileObservable() { /* as above */ } @Override protected void onDestroy() { subscription.unsubscribe(); }
Observable filePathObservable = downloadFileObservable().map(new Func1() { @Override public String call(File file) { return file.getAbsolutePath(); } }); // now emits file paths, not `File`s subscription = filePathObservable.subscribe( /* Observer */);
public void onNext(File file) { Toast.makeText(getActivity(), "Downloaded: " + file.getAbsolutePath(), Toast.LENGTH_SHORT) .show(); } public void onCompleted() {}
}
public void onError(Throwable error) { Toast.makeText(getActivity(), "Download failed: " + error.getMessage(), Toast.LENGTH_SHORT) .show(); }
RxJava on Android with Scala => AWESOME http:// pommedeterresautee.blogspot.jp/ 2013/11/rxjava-on-android-withscala-awesome.html
def getAsyncUrl(urlString: String) :Observable[Option[String]] = Observable { (observer: Observer[Option[String]]) => { val url = new URL(urlString) val urlCon = url.openConnection() urlCon.setConnectTimeout(5000) urlCon.setReadTimeout(5000) val io = Source.fromInputStream( urlCon.getInputStream) val result = Option(io.getLines(). mkString.split("\n")(0)) io.close() observer.onNext(result) observer.onCompleted() Subscription() } }
val mUnsubscribe = CompositeSubscription() mUnsubscribe += getAsyncUrl(lastScript) .execAsync.subscribe( _ match { case OnNext(Some(t)) => CInfo(t) case OnNext(None) => CAlert(getString( R.string.connection_error_message, "Server error")) case OnError(e) => CAlert(getString(R.string.connection_ error_message, e.getLocalizedMessage)) } )
Reactive extensions for .NET (Rx .NET) for Windows Phone http://msdn.microsoft.com/en-us/ library/windowsphone/develop/ ff431792%28v=vs.105%29.aspx
Part III Observableを活用する
Observableのストリームを 処理する Rx サンプル
Stock Trade Analysis MSFT 27.01
INTC 21.75
MSFT 27.96
ticks
from tick in ticks
MSFT 31.21
INTC 22.54
INTC 20.98
MSFT 30.73
Stock Trade Analysis MSFT 27.01
INTC 21.75
MSFT 27.96
MSFT 31.21
INTC 22.54
INTC 20.98
MSFT 30.73
ticks 27. 01
27. 96
30. 73
31. 21
MSFT 21. 75
INTC from tick in ticks
group tick by tick.Symbol
22. 54
20. 98
Stock Trade Analysis MSFT 27.01
INTC 21.75
MSFT 27.96
MSFT 31.21
[27.01, 27.96]
[27.96, 31.21]
INTC 22.54
INTC 20.98
MSFT 30.73
ticks [31.21, 30.73]
MSFT [21.75, 22.54]
INTC from tick in ticks group tick by tick.Symbol into company
from openClose in company.Buffer(2, 1)
[22.54, 20.98]
Stock Trade Analysis MSFT INTC MSFT Analysis MSFT INTC Stock Trade 27.01 21.75 27.96 31.21 22.54
INTC 20.98
MSFT 30.73
ticks 0.03 4
0.10 4
-0.0 15
MSFT 0.03 6
-0.0 69
INTC
from tick in ticks group tick by tick.Symbol into company from openClose in company.Buffer(2, 1)
let diff = (openClose[1] – openClose[0])/openClose[0]
Stock Trade Analysis MSFT 27.01
INTC 21.75
MSFT 27.96
MSFT 31.21
0.03 4
0.10 4
INTC 22.54
INTC 20.98
MSFT 30.73
ticks -0.0 15
MSFT 0.03 6
-0.0 69
INTC
from tick in ticks group tick by tick.Symbol into company from openClose in company.Buffer(2, 1) let diff = (openClose[1] – openClose[0])/openClose[0]
where diff > 0.1
Stock Trade Analysis MSFT 27.01
INTC 21.75
MSFT 27.96
MSFT 31.21
INTC 22.54
INTC 20.98
MSFT 30.73
ticks
res Company = MSFT Increase = 0.104
from tick in ticks group tick by tick.Symbol into company from openClose in company.Buffer(2, 1) let diff = (openClose[1] – openClose[0]) / openClose[0] where diff > 0.1
select new { Company = company.Key, Increase = diff }
Observableを変形・加工する 合成可能な関数を利用する
Composable Functions o Transform: map, flatmap, reduce, Scan ... o Filter: take, skip, sample, takewhile, filter ... o Combine: concat, merge, zip, combinelatest, multicast, publish, cache, refcount ... o Concurrency: observeon, subscribeon o Error Handling: onErrorreturn, onErrorResume ...
map
def map[B](f: A⇒B): Observable[B]
filter
def filter(p: A⇒Boolean): Observable[A]
merge
def merge(Observable[A],Obserbal[A]): Observable[A]
flatmap
def flatMap(f: T=>Observable[S]): Observable[S] = { map(f).flatten }
flatmap val xs: Observable[Int] = Observable(3,2,1)
val yss: Observable[Observable[Int]] = xs.map(x => Observable.Interval(x seconds) .map(_=>x).take(2)) val zs: Observable[Int] = yss.flatten() xs: Observable[Int]
yss: Observable [Observable[Int]]] merge zs: Observable[Int]
concat
val xs: Observable[Int] = Observable(3,2,1) val yss: Observable[Observable[Int]] = xs.map(x => Observable.Interval(x seconds) .map(_=>x).take(2)) val zs: Observable[Int] = yss.concat() xs: Observable[Int]
yss: Observable [Observable[Int]]] zs: Observable[Int]
zip
groupBy
def groupBy[K](keySelector: T⇒K) : Observable[(K,Observable[T])]
startWith
def startWith(ss: T*): Observable[T]
reduce
def reduce(f:(T, T) ⇒T): Observable[T]
ServerサイドでのRxの利用 Netflix --- RxJava “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”
RxJava Java 8へのRxの移植 Observable.toObservable("one”,"two”,"three") .take(2) .subscribe( (arg) -> { System.out.println(arg); } );
https://github.com/Netflix/RxJava
Netflix o Netflixは、北米のインターネットのダウンロード ・トラフィックの33% を占める。 o Netflixの顧客は、一月に10億時間以上、テレ ビを見ている。 o APIのトラフィックは、2010年の2000万/一日 から、2013年には 20億/一日に成長した。
一日の Netflix APIへのリクエスト数
o クライアントのデバイスは、Netflixの二つの主 要なサービスに接続する。第一のサービスは、 ビデオの検索やそのコンテンツの紹介に関係し た機能を提供するNetflix APIである。第二の サービスは、ビデオ・ストリームの再生サービス である。 o ここでは、ビデオの検索・発見にかかわるNetflix APIについて見ていく。
Rxの発見は、システムの Re-Atchitectureから始まった
ネットワーク・トラフィックは、 APIの改良で、劇的に改善した
9つあったネットワークの呼び出しは 1つになり、WANでのネットワーク 遅延は一回だけになった
クライアントのロジ ックは、サーバに移 され、20以上の呼び 出しが削除された
関連情報 o Netflix API architecture http://techblog.netflix.com/search/label/api https://speakerdeck.com/benjchristensen/ o Re-architecture http://techblog.netflix.com/2013/01/ optimizing-netflix-api.html o Hystrix https://github.com/Netflix/Hystrix
NetflixでのObservableの利用 Netflixでは、ブロックするAPIをObservable を返す非同期のAPIに作り替え、ついで、それら のObservableを返すAPIを組み合わせてサ ービスを構成した。
Observableを作る Observable.create({ observer ‐> try { observer.onNext(new ....)) observer.onCompleted(); } catch(Exception e) { observer.onError(e); } }) Observable create(Func1, Subscription> func)
def Observable getRating(userId, videoId) { return Observable.create({ observer ‐> executor.execute(new Runnable() { def void run() { try { VideoRating rating = //.. do network call . observer.onNext(rating) observer.onCompleted(); } catch(Exception e) { observer.onError(e); } } }) Asynchronous Observable }) } with Single Values
def Observable getVideos() { return Observable.create({ observer ‐> executor.execute(new Runnable() { def void run() { try { for ( id in videoIds ) { Video v = // .. do network call ... observer.onNext(v) } observer.onCompleted(); } catch(Exception e) { observer.onError(e); } } Asynchronous Observable }) }) with Multiple Values }
Observableを組み合わせて、サービスを作る
こうした情報を返すサービス
Observableを組み合わせて、サービスを作る
Observableは、OnNextにn個のビデオを送り出す
def Observable getVideos(userId) { return VideoService.getVideos(userId) }
Observableは、OnNextにn個のビデオを送り出す
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) }
最初の10個を取って、あとはunscribeする。 10個のビデオを送り出す Observableを返す。
Observableを組み合わせて、サービスを作る
最初の10個を取って、あとはunscribeする。 10個のビデオを送り出す Observableを返す。
Observableを組み合わせて、サービスを作る
unscribe
最初の10個を取って、あとはunscribeする。 10個のビデオを送り出す Observableを返す。
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) .map( { Video video -> // Videoオブジェクトを変形する }) }
map operatorは、入力値を他の出力値に変える
Observable b = Observable.map({ T t -> R r = ... transform t ... return r; })
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) .flatMap( { Video video -> // それぞれのビデオからメタデータを取り出す def m = video.getMetaData() .map( { Map md -> return [ title: md.get(“title”), length: md.get(“duration”)] }) // ブックマークとレイティングも def b .... def r .... }) } ここでは、flatMap / mapMany を使う。 mapでは Observableの 要素一つが、型 Rの要素一つに変わったが、flatMap / mapManyでは、 Observableの要素一つ一つが、別のObservableに変わる。
Observableを組み合わせて、サービスを作る
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) .mapMany( { Video video -> // それぞれのビデオからメタデータを取り出す def m = video.getMetaData() .map( { Map md -> return [ title: md.get(“title”), length: md.get(“duration”)] }) // ブックマークとレイティングも def b .... mapManyは、三つの関数の定義に応じて def r .... Observableから、次の三つの }) Observableを生成する。 }
Observable Observable Observable
Observableを組み合わせて、サービスを作る
一つのVideo ‘v’に対して getMetaData()は、 Observableを返す
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) .mapMany( { Video video -> // それぞれのビデオからメタデータを取り出す def m = video.getMetaData() .map( { Map md -> return [ title: md.get(“title”), length: md.get(“duration”)] }) // ブックマークとレイティングも def b .... def r .... }) }
それぞれのObservableは、mapを利用して データを変換する
Observableを組み合わせて、サービスを作る
Observableは、map関数で変形される
Observableを組み合わせて、サービスを作る
Observable、Observableも 同様に処理される
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) .mapMany( { Video video -> // それぞれのビデオからメタデータを取り出す def m = video.getMetaData() .map( { Map md -> return [ title: md.get(“title”), length: md.get(“duration”)] }) // ブックマークとレイティングも def b .... def r .... // これらを結合する }) }
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) .flatmap( { Video video -> // それぞれのビデオからメタデータを取り出す def m ... def b .... def r .... // これらを結合する }) }
def Observable getVideos(userId) { return VideoService.getVideos(userId) // リストの最初の10本だけが欲しい .take(10) .flatmap( { Video video -> // それぞれのビデオからメタデータを取り出す def m ... def b .... def r .... // これらを結合する return Observable.zip( m, b, r, { metadata, bookmark, rating -> // 完全なデータの辞書に変換する return [ id: video.videoId ] << metadata << bookmark << rating }) zipは、三つの非同期のObservableを }) 一つのObservableにまとめる }
Observableを組み合わせて、サービスを作る
Observableを組み合わせて、サービスを作る
public Data getData(); を考える -- NetFlix RxJava もしも、同期型から非同期型に変わったら、 実装はどう変化するだろうか? クライアントは、ブロックしないように実行す るには、どうすればいいだろうか?
getDataの実装の候補 o public Data getData(); o public void getData(Callback c); o public Future getData(); o public Future>> getData(); o ...
?
IterableとObservable Iterable Observable Pull Push T next() onNext[T] throw Exception onError[Exception] returns; onCompletion
IterableとObservable o 同じ高階関数が、IterableにもObservable にも、同じように適用出来る。
getData()の分類 o 同期/非同期、返り値の単数/複数で表を作っ てみると、getData()の次のような分類が可能 である。
String s = getData(args); if (s.equals(x)) { // do something } else { // do something else } scalarを返り値に持つ、典型的な同期型 getData()のコード
Iterable values = getData(args); for (String s : values) { if (s.equals(x)) { // do something } else { // do something else } } scalar値が返る場合とほとんど同じだが、処理はループする
Future s = getData(args); if (s.get().equals(x)) { // do something } else { // do something else } 古いJavaのFutureコード。非同期だが、getでブロックする。
Future s = getData(args); Futures.addCallback(s, new FutureCallback { public void onSuccess(String s) { if (s.get().equals(x)) {...} else {...} } public void onFailure(Throwable t) { // handle error } }, executor);
Futureとcallbackをつかった非同期のコード・サンプル
Observable s = getData(args); s.map({ s -> if (s.get().equals(x)) { // do something } else { // do something else }); Observableを使ったgetData()のコード・サンプル
IterableとObservableの「双対性」 o Observable/Observer は、同期型の Iterable/Iterator の非同期の双対形である。 o “Subject/Observer is Dual to Iterator” Erik Meijer ”http://csl.stanford.edu/~christos/pldi2010.fit/ meijer.duality.pdf
o “Introduction to the Reactive Framework Part II ” http://codebetter.com/matthewpodwysocki/2009/11/03/ introduction-to-the-reactive-framework-part-ii/
参考資料 Reactive Manifesto
Reactive Manifesto 2013年9月23日 version 1.1 http://www.reactivemanifesto.org/
Reactiveに移行する必要性 o 近年、アプリケーションの要請は劇的に変わってきた。 ほんの数年前には、大規模なアプリケーションは、数十 台のサーバ、数秒のレスポンスタイム、数時間のオフラ イン・メンテナンス、数ギガバイトのデータから構成され ていた。今日、アプリケーションは、モバイル・デバイス から数千のマルチコア・プロセッサーが走るクラウド・ベ ースのクラスターにいたるまであらゆるところに配備さ れている。ユーザはミリセカンドさらにはナノセコンドの レスポンスタイムと、100%の稼働を期待している。デ ータの要求は、ペタバイトに拡大しようとしている。
o 当初は、GoogleやTwitterといった革新的なインターネ ット・ドリブンの企業の領域だけだったが、こうしたアプリ ケーションの特徴は、ほとんどの産業で表面化した。金 融や通信事業者が、最初に新しい要請を満たす実践を 受け入れ、その他の産業がそれに続いた。 o 新しい要請は、新しいテクノロジーを要求する。それ以 前のソリューションは、管理されたサーバとコンテナに 力点を置いたものだった。規模拡大は、より大きなサー バを買うことと、マルチスレッドによる並行処理を通じて 達成された。追加されたサーバは、複雑で非効率かつ 高価なプロプライエトリなソリューションを通じて追加さ れた。
o しかし今では新しいアーキテクチャーが進化し、開発者 が今日の要求を満たすアプリケーションを概念的に把 握し、アプリを構築するのを可能にしている。我々は、こ れらをリアクティブ・アプリケーションと呼んでいる。この アプリケーションは開発者が、イベント・ドリブンで、拡張 性を持ち、耐障害性の高い、応答性に優れたシステム を構築することを可能とする。すなわち、このアプリケー ションは、リアルタイム感に富み、拡張可能で耐障害性 の高いアプリケーション層で支えられ、マルチコアでもク ラウド・コンピュータのアーキテクチャでもすぐに配備さ れうる、高い応答性のユーザ体験を提供出来るアプリ ケーションである。 このReactive Manifestoは、リア クティブに移行するのに必要な本質的な諸特徴を記述 するものである。
Reactive アプリケーション o 辞書のMerriam-Websterは、reactiveを「刺激に対 してすぐに反応できること」と定義している。すなわち、 そのコンポーネントは「アクティブ」で、常にイベントを受 け取る準備ができているということである。この定義は、 リアクティブなアプリケーションの本質を捉えている。そ れはシステムの次のようなところに焦点を合わせている。
o react to events イベントに反応する イベント・ドリブンの性質は、次のような特質を 可能にする o react to load 負荷に反応する 共有リソースの競合を避けることで、拡張可能 性にフォーカスする o react to failure 失敗に反応する 全てのレベルでエラー回復の能力を持たせるこ とで、耐障害性の高いシステムを構築する o react to users ユーザに反応する 負荷のいかんにかかわらず、高い反応時間を 保証する
o これらの一つ一つが、リアクティブなアプリケーションの 本質的な特徴である。一方で、それらのあいだには、相 互に従属関係が存在する。ただ、これらの特徴は、標 準的な階層型のアプリケーションの意味での「層」にあ たるものではない。その代わりに、それらは全ての技術 をつらぬいた設計の諸特徴を記述したものである。
Event-driven 重要なこと o 非同期通信に基づいたアプリケーションは、疎結合デザ インを実装する。それは、純粋に同期型のメソッド呼び 出しに基づいたアプリケーションよりもいいものである。 イベントの送り手と受け手は、如何にイベントが伝播す るのかの詳細を考えなくても実装されることが出来る。 インターフェースは通信の内容に集中出来る。このこ とで、拡張も改良も保守も、より容易な実装ができるよう になり、開発の柔軟性を増やすと同時に保守コストを低 減出来る。
o 非同期通信のメッセージの受け手は、イベントが起きる まで、あるいは、メッセージを受け取るまで、眠っている ことが出来るので、イベント・ドリブンのアプローチは、 多数の受け取り手が、単一のハードウェア上のスレッド を共有することで、既存のリソースを有効に利用するこ とが出来る。 重い負荷の下にある、ブロックしないアプリケーションは 、こうして、ブロックする同期型の通信単位に基づいた 伝統的なアプリケーションより、低遅延と高スループット を持つことが出来る。結果として、オペレーションのコス トは低いものになり、可用性は高まり、エンドユーザはハ ッピーになる。
Event-driven キーとなる構成ブロック o イベント・ドリブンのアプリケーションでは、様々な事実を 記述する離散型の情報の断片であるイベントの生産と 消費を通じて、コンポーネントは相互作用を行う。これら のイベントは、ブロックしない非同期なスタイルで、送ら れ受け取られる。イベント・ドリブンのシステムは、Pullや Pollより、Pushに依存することが多い。すなわち、イベン トの消費者にデータをいつまでも要求するか待たせるこ となく、それが利用可能になった時点で消費者に向けて データをPushする。
o 非同期のイベントの送信は、メッセージ・パッシングとも 呼ばれるのだが、それはアプリケーションが設計から高 度に並列化され、変更なしでマルチコアのハードウェア を利用出来るということを意味する。CPU内の全てのコ アがいかなるメッセージ・イベントをも処理出来るというこ とは、並行実行の可能性を劇的に増大させる。 o ノン・ブロッキングは、エラーや処理の爆発的な増大が 起きた場合でさえも、アプリケーションが、どんな時でも 反応を返すことが出来る為に、処理を連続的に進める 能力を意味する。この為には、反応に必要とされる全て のリソースは—例えば、CPU、メモリー、ネットワーク 等は、独占されてはならない。こうして、それは低い遅延 と高いスループットとより良い拡張性を可能とする。
o 伝統的なサーバサイドのアーキテクチャーは、変更可 能な状態の共有と単一スレッド上のブロック操作に依存 している。両者はともに、こうしたシステムを変化する要 求に適合するようにスケールしようとした時遭遇する様 々の困難の原因となる。変更可能な状態の共有は、同 期を要求する。これは、思わぬ複雑さと非決定性を導 入することになり、プログラムのコードの理解を難しいも のにし、保守も難しいものにする。ブロッキングでスレッ ドを眠らせることは、有限の資源を浪費し、また、起き上 がらせる時に高いコストを払うことになる。
o イベントの生成と処理を分離することで、ランタイムのプ ラットフォームは、同期の詳細とスレッドをまたいでいか にイベントが送り出されるかを意識することが可能となる 。一方で、プログラムの抽象はビジネスのワークフロー のレベルまで持ち上げられる。スレッドやロックといった 低レベルの諸機能にかかわることなしに、イベントがど のようにシステムの中を伝播し、コンポーネントがどのよ うに相互作用するかを考えればいい。 o イベント・ドリブンのシステムは、コンポーネント間、サブ システム間の疎結合を可能とする。この間接性のレベ ルは、あとで見るように、拡張可能性と耐障害性の前提 条件の一つである。コンポーネント間の複雑で強い従 属性を取り除くことで、イベント・ドリブンのアプリケーショ ンは、既存のアプリケーションに最低限のインパクトを 与えるだけで拡張が可能である。
o アプリケーションが、高いパフォーマンスと大規模な拡 張可能性を求める要求の圧力のもとにおかれた時、ど こにボトルネックが生ずるかを予測するのは難しい。そ れ故、ソリューション全体が非同期でノン・ブロッキング なものであることが重要である。典型的な例では、この ことは、UIのユーザのリクエスト(ブラウザーなり、RES Tのクライアントなりなんでも)からリクエストのパージング とweb層での送り出しまで、ミドルウェアのサービス・コ ンポーネントに取っては、キャッシングからデータベース にいたるまで、デザインがイベント・ドリブンである必要 があることを意味する。 o もしこれらの層の一つでもこれに参加せず、データベー スにブロックするような呼び出しを行ったり、変更可能な 共有状態に依存したり、高価な同期処理を行ったとす れば全てのパイプラインはとどこおり、ユーザは遅延の 増大と拡張可能性の減少に苦しめられるだろう。
o アプリケーションは、その全体がリアクティブでなければ ならない。 この鎖の一番弱い環を取り除くことの必要性は、 Amadahlの法則に、よく表現されている。この法則は、 Wikipediaによれば次のように説明されている。 o パラレル・コンピューティングで複数のプロセッサーを使 ったプログラムのスピードアップは、そのプログラムのシ ーケンシャルな部分によって制限付けられる。例えば、 プログラムの95%が並列化されたとしても、パラレル・ コンピューティングを使った理論上の最大のスピードア ップは、次の図に示すように、どんなに多くのプロセッサ ーを使ったとしても、20倍にとどまる。
Amdahl's Law
Scalable 重要なこと o “scalable”という言葉は、Merriam-Websterによ れば、次のように定義されている。 “要求に応じて、容 易に拡張ないしは格上げが可能であること”. 拡張可能なアプリケーションは、その利用に応じて拡張 することが出来る。これは、アプリケーションに柔軟性を 付加することで達成出来る。要求に応じてスケール・ア ウトまたはスケール・イン(ノードを追加または削除する )することが可能なオプションである。さらに、アーキテク チャは、アプリケーションの再デザインや書き直しなしで 、スケール・アップまたはスケール・ダウン(ノードにより 多くのあるいはより少ないCPUを配備する)することを 容易にする。 柔軟性は、アプリケーションをクラウド環 境で走らせるコストを最小化する。そして「使った分だけ 払う」というモデルで、利益を得ることができる。
o 拡張可能性はまたリスクを管理するのを助ける。ユー ザの負荷に対してあまりにも少ないハードウェアしか提 供しないのは、ユーザの不満足を引き起こし、顧客を失 うことになる。逆に、理由もなく遊んでいる多くのハード ウェアとオペレーションの要員を与えるのは、不必要な 経費でしかない。拡張可能なソリューションは、使うにふ さわしい新しいハードウェアを使うことが出来ずに、アプ リケーションを使い続けるリスクを緩和する。10年以内 には、我々は、ことに数千ではないにしろ数百のハード ウェア・スレッドが走るプロセッサーを見ることになるだ ろう。こうした潜在的な可能性は、アプリケーションが、 非常に細かな粒度のレベルでスケーラブルであること を要求している。
Scalable キーとなる構成ブロック o 非同期のメッセージ・パッシングに基づいたイベント・ドリ ブンのシステムは、拡張可能性の基礎を提供する。コン ポーネントとサブシステム間の疎結合と位置独立性は、 同じセマンティックをもった同じプログラミング・モデルを 維持したまま、システムを複数のノードにスケールアウ トすることを可能にする。 コンポーネントのインスタンス を追加することで、システムのイベント処理の能力は増 大する。実装に関して言えば、複数のコアを利用してス ケール・アップすることと、データセンターやクラスターで 沢山のノードを利用してスケール・アウトすることの間に 違いはない。
o アプリケーションのトポロジーは、アプリケーションの利 用に応じた配置やランタイムのアルゴリズムの採用を通 じて表現される配備の決定になる。このことを、位置透 過性と呼ぶ。 o 目標は、透過的な分散コンピューティング、分散オブジ ェクト、RPCスタイルの通信を実装しようと試みることな どではないのだと理解することは重要である。こうしたこ とは以前に試みられ、そして失敗してきた。我々に必要 なことは、非同期メッセージ・パッシングを通じたプログラ ミング・モデルで直接に表現されるネットワークを擁護す ることなのである。真の拡張可能性は、分散コンピュー ティングを自然に含み、そしてそこには、本質的に信頼 出来ないと言っていい、ネットワークをまたぐことを意味 するノード間の通信が含まれている。
o それゆえ、プログラミング・モデルの中で、物事を「簡単 にする」という見落としの多い抽象の背後にそれらを隠 すことなく、ネットワーク・プログラミングの制約やトレー ドオフや失敗のシナリオを明確にすることは重要である。 結果として、分散環境で起きてくる典型的な問題を解決 する為の共通の構成ブロックをカプセル化するツール – 高い段階の信頼性を提供する合意の形成やメッセー ジの抽象を与えるようなツールを提供することは、同じよ うに重要なことである。
Resilient 重要なこと o アプリケーションのダウンは、ビジネスに起きうるもっと もダメージの多い問題の一つである。通常の帰結は、オ ペレーションが単に止まっただけでも、収入の流れに穴 があいたままになるということである。長い期間でみ れば、それは顧客の不満と悪い評判につながって、ビ ジネスをもっと深刻に傷つけることになりかねない。アプ リケーションの対障害性が、大部分では無視されたり、 経験的なテクニックで対応されている要請であることは 驚くべきことである。このことは、あまりに荒い粒度のツ ールを使うという粒度のレベルの間違いに基づいている ことを意味する。
o 実行時やサーバのエラーからの回復の為に、通常は、 アプリケーション・サーバのクラスタリングの手法を用 いる。不幸なことに、サーバのフェール・オーバーは、非 常にコストがかかる、しかも危険なものである。それは、 潜在的には、失敗の連鎖をおこして全てのクラスターの 停止を招きかねない。コンポーネント・レベルの細粒度 の耐障害性を利用して対応すべきことを、事故管理の 間違ったレベルの粒度で対応しているというのは、こう した理由に基づく。 o Merriam-Websterは、耐障害性を次のように定義し ている。 n 実体あるいはオブジェクトの、もとの形に戻ろうとす る能力。 n 困難からすぐに立ち戻る能力
o リアクティブ・アプリケーションでは、耐障害性は結果論 ではなく、最初からデザインの一部である。失敗を、プロ グラミング・モデルの中で、第一級のクラスの構成物と することは、それに反応しそれを管理する手段を提供す ることを意味する。それは、アプリケーションを、それら を実行時に修復し回復する能力を持たせることで、失敗 に対して高い耐久性を持たせることになる。伝統的な障 害の処理は、こうしたことを達成出来ない。なぜなら、そ れは、例外が発生した時にはいつでもどこでもそれを処 理するか、あるいは、障害が起きたらアプリケーション の全てのインスタンスをフェールオーバーするかというこ とで、小規模なものではあまりに防御的で、大規模なも のにはあまりに攻撃的だからである。
Resilient キーとなる構成ブロック o 失敗を管理する為には、それが他の健全なコンポーネン トに広がらないようにそれを隔離する手段と、また、失 敗のコンテキストの外側の安全な地点からそれを観察 する手段を必要とする。思い浮かぶ一つのパタンは、次 の絵に示すような「隔壁」というパターンである。そこでは 、システムはそれらに一つで事故が発生しても他の部 分は影響を受けないように、安全な区域によって構成さ れている。これは、古典的な問題である事故の連鎖を 防止し、管理の問題を隔離することを可能にする。
o 拡張可能性を可能とするイベント・ドリブン・モデルは、 事故管理のこのモデルを実現する為の必要な基本的 な単位でもある。イベント・ドリブン・モデルの疎結合は、 事故はそのコンテキストとともに補足されるとともに、メ ッセージとしてカプセル化され、他のコンポーネントに送 られてエラー検知を可能にしそれにどう対応するか決 定出来るように、完全に隔離されたコンポーネントを提 供する。
o このアプローチは、ビジネス・ロジックはクリーンに残し、 予期しなかったことの処理から分離したままで、事故は 区分けされ観察され管理され、そして宣言的な仕方で 処理されるようなシステム、そこでは、システムは自分 自身を自動的に修復し回復出来るようなシステムを作 り出す。それは、大きな企業では、問題はそれを処理す る権限を持ったレベルの部署に到達するまで上層部に 伝えられるのとほとんど同じように、区分けが階層的な 仕方で構造化されていると最もうまく機能する。 o このモデルの美しい点は、それが純粋にイベント・ドリブ ンで、リアクティブなコンポーネントと非同期のイベント に基づいており、それ故、位置通過的であることである。 実践的には、このことは、分散環境においても、ローカ ルなコンテキストでの同じセマンティックでそれが動くこ とを意味する。
Responsive 重要なこと o Responsiveは、 Merriam-Webster によれば「応答 が素早く、適切に反応すること」と定義されている。我 々は、この言葉をそうした一般的な意味で用いる。そ れは、Webデザインにおける Responsive Web Design と混同されてはならない。それは、もともとは、 CSSのmedia queryとその改良・強化にかかわるも のだ。 o Responsiveアプリケーションは、リアルタイムで魅力 的で豊かで共同的なものだ。ビジネスはその顧客と、レ スポンシブでインタラクティブな経験を通じて顧客を歓 迎することで、オープンで進行する対話を作り出すこと が出来る。
o それはビジネスをより効率的なものにし、人々はつなが っていて問題を解決し課題を達成する手段を持っている という感覚を作り出す。一つの例が、Google Docで ある。 それはユーザがドキュメントを共同で編集するこ とを可能とし、リアルタイムに、参加者が互いに編集やコ メントを、書いたらすぐに生き生きと見ることが出来る。 o イベントに反応するアプリケーションは、たとえ事故があ ったとしてもタイムリーな仕方でそれを行う必要がある。 もし、アプリケーションが、別の言い方をすると遅延と見 なされる、適切な時間の制約以内で反応しなかったら、 それは実際には使い物にはならなくて、それ故、耐障害 性を持つとは考えられなくなる。
o 厳しい実時間システムの制約に応えることが出来ないこ とは、軍事や医療コントロールシステムのようなアプリケ ーションの場合には、システム全体の事故と見なされる 。全てのアプリケーションが、こうした厳しい要請を持つ 訳ではない。多くのアプリケーションは、応答時間の制 約から外れると、急速に有用性が失われるのを見ること になる。例えば、金融トレーディングのアプリケーショ ンは、タイムリーな応答なしには、現在の取引を失うこと になる。 o 小売りやネット購買のような、主流のより多くのアプリケ ーションは、反応時間が遅くなるにつれ確実に有用性 が落ち始める。ユーザが、レスポンシブなアプリケーショ ンと相互作用すればするほど、購買のボリュームは増 大する。
Responsive キーとなる構成ブロック o リアクティブなアプリケーションは、Observableなモデ ルとイベント・ストリーム、クライアントの状態を利用する。 o Observableモデルは、状態が変わった時に、他のシ ステムがイベントを受け取ることを可能とする。このこ とは、ユーザとシステムとのあいだにリアルタイムの接 続を提供する。例えば、複数のユーザが同じモデルで 同時に仕事をしていた時、変化は彼らの間でリアクティ ブに双方向に同期されうる。こうして、そのモデルはロッ クの制約なしに共有されているように見える。
o イベント・ストリームは、その上にこうした接続が構築さ れる基本的な抽象を形成する。これらをリアクティブに することは、ブロッキングを排除して、その代わりに、非 同期でノンブロッキングな転送と通信を可能とする。 o リアクティブ・アプリケーションは、デザイン・パターンと テストを利用してアルゴリズムの順序を守る。これによ って負荷のいかんにかかわらずイベントへの応答がO(1 )あるいは少なくともO(log n)時間で返ることを保証する 。このスケール因子には、顧客、セッション、製品、取引 を含むことが出来るが、それに限られない。
o それらは、負荷のプロファイルのいかんにかかわらず 応答の遅延を一定にするように、いくつかの戦略を利用 している。 o 爆発的なトラフィックの条件下では、リアクティブ・アプリ ケーションは、遅延を一定のものに保つ為に、リソース をある了解と配慮の下で、結合したバッチ処理を適用 して、IOや同時並行でのデータの交換のような高価な 処理を、後回しにする。 o キューは適切なバックの圧力で制限されている。キュー の長さは、与えられたレスポンスの制約条件の下で、 Littleの法則を適用することで決定される。 o システムは、その場で計画された適切な容量でモニター される。 o 事故は、回路遮断機がトリガーされると、あらかじめ利 用可能にされている代替処理戦略によって隔離される。
o レスポンシブ・アプリケーションの例として、ブラウザ・ベ ースであれモバイル・アプリであれ、魅力的なユーザ経 験を作り出す、リッチ・クライアントのWebアプリを考え よう。このアプリケーションは、クライアント側でロジック を実行し状態を記憶しているしよう。Observableモデ ルは、データが変更された時、リアルタイムでユーザ・イ ンターフェースを変更するメカニズムを提供する。 WebSocketやServer-Sent-Eventのような技術が、 ユーザ・インターフェースを直接にプッシュされたイベント ・ストリームに接続することを可能とする。こうして、イベ ント・ドリブン・システムは、バックエンドからクライアント にいたるまで、あらゆる方法で拡張される。このことが、 リアクティブ・アプリケーションが、非同期でノン・ブロッキ ングなデータ転送を使って、ブラウザやモバイル・アプ リに、スケーラブルで耐障害性を持ったやり方で、イベン トをプッシュすることを可能とする。
o このことを心に留めておけば、いかにして四つの特質、 イベント・ドリブン、拡張可能性、耐障害性、レスポンシ ブが、相互に関連し全体として密接に結びついているこ とは、明らかになる。
結論 o リアクティブ・アプリケーションは、ソフトウェア開発の現 代における幅広い挑戦に向けられたバランスの取れた アプローチを表現している。イベント・ドリブン、メッセージ ・ベースの基礎の上に硬直されることで、それは拡張可 能性と耐障害性を保証するのに必要なツールを提供し ている。これらの上に、それは豊かでレスポンシブなユ ーザ・インタラクションをサポートしている。我々は、この 数年以内にこの青写真に従うシステムの数が、急速に 増えていくことを期待している。