Skip to main content
Reactive Webアプリケーション
- そしてSpring 5へ
Toshiaki Maki (@making)
JJUG CCC 2015 Fall
2015-11-30 #jjug_ccc
#ccc_ef3
聞いたことあります?
• Reactive Programming?
• Reactive Manifest?
• Reactive Streams?
• Reactive Extensions?
• Reactive ... ?
• Reactiveが重要視されてきた背景
• Reactive Streams
• Reactive Extensions
• Spring 5
今日話すこと
Reactive
Extensions(Rx)
Reactive
Programming
Reactive
Streams
Reactor
RxJava
Spring 5
プロラミング
パラダイム
仕様(?)
今日話すこと
実装
ライブラリ
Spring
Framework
元ネタ
http://www.slideshare.net/SpringCentral/reactive-web-applications-53170985
http://www.slideshare.net/SpringCentral/introduction-to-reactive-programming
Reactiveが重要視され
てきた背景
Amdalの法則
https://en.wikipedia.org/wiki/Amdahl%27s_law
Amdalの法則
https://en.wikipedia.org/wiki/Amdahl%27s_law
プログラムの並列化による速度の向上は、
プログラムの逐次処理部分によって制限される
Amdalの法則
https://en.wikipedia.org/wiki/Amdahl%27s_law
プログラムの並列化による速度の向上は、
プログラムの逐次処理部分によって制限される
プログラム中の
並列実行可能部分が50%
だと、いくら並列度を増や
しても2倍の性能向上し
か見込めない
スケールアウト戦略
• メモリばかり消費してCPUに空き…
✦ 大量のスレッドプール
✦ブロッキングI/O
ブロッキングコードを
書いていては
ハードにいくら金をかけても
性能向上に限界がある
Blocking is evil
• ブロッキングコードは並列化の妨げ
• 2大ブロッキング
• リクエストの受信待ち/レスポンス
の送信待ち
• DBクエリの結果待ち
Blocking is evil
• ブロッキングコードは並列化の妨げ
• 2大ブロッキング
• リクエストの受信待ち/レスポンス
の送信待ち
• DBクエリの結果待ち
InputStream
OutputStream
JDBC
• IoT
• マイクロサービス
今後のトレンド
• IoT
• マイクロサービス
低速で膨大な
リクエスト
が予想される
今後のトレンド
• IoT
• マイクロサービス
低速で膨大な
リクエスト
が予想される
今後のトレンド
Blocking == 💴
Non-Blocking!
• 命令型ではなくリスナー/コールバックス
タイル
• スレッド数を少なく
• →省メモリ
• →コンテキストスイッチを減らす
• →CPUを有効活用
Reactiveプログラミング
• 命令型と異なるプログラミングパラダイム
• 連続的なデータをイベントハンドラで処理する
• データフロー(データ同士の関係性)に着目した
イベントドリブンなプログラミング
Reactiveプログラミング
• 命令型と異なるプログラミングパラダイム
• 連続的なデータをイベントハンドラで処理する
• データフロー(データ同士の関係性)に着目した
イベントドリブンなプログラミング
ノンブロッキングコードを書く
のに向いている
最も身近なReactiveプログラミング?
最も身近なReactiveプログラミング?
最も身近なReactiveプログラミング?
最も身近なReactiveプログラミング?
最も身近なReactiveプログラミング?
A, Bの変更に伴い
Cも変わる
データフロー
A
B 2
+ C
A, Bの変更に伴い
Cも変わるhttps://speakerdeck.com/okapies/reactive-streams-ru-men-number-jjug
イベントストリーム
x2
+
1  2      4      3
9    6    8    6    5    9
8      4      2  64      2      1  3
A
B
C
イベントストリーム
x2
+
1  2      4      3
9    6    8    6    5    9
8      4      2  64      2      1  3
A
B
C
イベントストリーム
x2
+
1  2      4      3
9    6    8    6    5    9
8      4      2  64      2      1  3
A
B
C
イベントストリーム
x2
+
1  2      4      3
9    6    8    6    5    9
8      4      2  64      2      1  3
A
B
C
イベントストリーム
x2
+
1  2      4      3
9    6    8    6    5    9
8      4      2  64      2      1  3
A
B
C
イベントストリーム
x2
+
1  2      4      3
9    6    8    6    5    9
8      4      2  64      2      1  3
A
B
C
イベントストリーム
x2
+
1  2      4      3
9    6    8    6    5    9
8      4      2  64      2      1  3
A
B
C
命令型
int A = 3;
int B = 3;
int C = A + B * 2; // 9
B = 1;
A, Bが変更されても
Cは変わらない
JavaFXの場合
@FXML

Spinner<Integer> valueA;

@FXML

Spinner<Integer> valueB;

@FXML

Label valueC;
private void bind() {

IntegerProperty a = new SimpleIntegerProperty(0);

IntegerProperty b = new SimpleIntegerProperty(0);

NumberBinding c = a.add(b.multiply(2)); // c = a + 2 * b

a.bind(this.valueA.valueProperty());

b.bind(this.valueB.valueProperty());

valueC.textProperty().bind(c.asString());

}
http://aoe-tk.hatenablog.com/entry/2015/07/12/173402
A, Bの変更に伴い
Cも変わる
Reactive Webアプリケーション
Repository
DBService
Controller
Reactive Webアプリケーション
Repository
DBService
Controller
Reactive Webアプリケーション
Repository
DBService
Controller
Blocking Blocking
Reactive Webアプリケーション
Repository
DBService
Controller
Blocking BlockingBlocking
他サービス呼び出し
Reactive Streams
Reactive Streams
• http://www.reactive-streams.org/
• 非同期ストリームの標準仕様
• BackPressure付き
Reactive Streamsの動機
1. 技術面: データを送り側の勢いが受け
取り側の処理速度より速い場合に、デー
タが れることを防ぎたい
(BackPressure)
2. 開発面: 似たような機能(API)を持つ製
品群の相互互換性を持たせたい
関わっている会社
• Typesafe
• Pivotal
• Netflix
• RedHat
• など
関わっている会社
• Typesafe ... Akka Streams
• Pivotal ... Reactor
• Netflix ... RxJava
• RedHat ... Vert.x
• など
Reactive Streamsが提供するもの
• 4つのインターフェース
• 仕様ドキュメント
• TCK (仕様をテストするJUnitの親クラス)
public interface Publisher<T> {
public void subscribe(Subscriber<? super T> s);
}
public interface Subscription {
public void request(long n);
public void cancel();
}
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
public interface Processor<T, R> extends
Publisher<T>, Subscriber<R> {}
org.reactivestreams
public interface Publisher<T> {
public void subscribe(Subscriber<? super T> s);
}
public interface Subscription {
public void request(long n);
public void cancel();
}
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
public interface Processor<T, R> extends
Publisher<T>, Subscriber<R> {}
org.reactivestreams
BackPressure
呼び出し順
onSubscribe onNext* (onError | onComplete)?
Subscription SubscriberPublisher Application
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
request(4)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
request(4)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
request(4)
onNext(●)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
request(4)
onNext(●)
onNext(●)
onComplete()
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(Long.MAX_VALUE)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(Long.MAX_VALUE)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(Long.MAX_VALUE)
onNext(●)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(Long.MAX_VALUE)
onNext(●)
onNext(●)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(Long.MAX_VALUE)
onNext(●)
onNext(●)
onNext(●)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(Long.MAX_VALUE)
onNext(●)
onNext(●)
onNext(●)
onNext(●)
onNext(●)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(Long.MAX_VALUE)
onNext(●)
onNext(●)
onNext(●)
onNext(●)
onNext(●)
onComplete()
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
request(4)
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
request(4)💣
💥
Subscription
subscribe(Subscriber)
SubscriberPublisher Application
onSubscribe(Subscription)
request(1)
onNext(●)
request(2)
onNext(●)
onNext(●)
request(4)
onError(Throwable)
💣
💥
RxJavaからinspired
RxJava Reactive Streams
Observable<T> Publisher<T>
Observer<T> Subscriber<T>
Subscription Subscription
Non BlockingなAPI
• TやList<T>の代わりにPublisher<T>を返す
Blocking Non Blocking
User
get(String name)
Publisher<User>
get(String name)
List<User>
allFriends(User user)
Publisher<User>
allFriends(User user)
実装例 (Publisher)
public class IntPublisher implements Publisher<Integer> {

@Override

public void subscribe(Subscriber<? super Integer> s) {

s.onSubscribe(new Subscription() {

Iterator<Integer> it = IntStream.range(0, 10)
.boxed().iterator();

@Override

public void request(long n) {

for (int i = 0; i < n; i++) {

if (it.hasNext()) {

s.onNext(it.next());

} else {

s.onComplete();

break;

}}}

@Override

public void cancel() {}

});}}
実装例 (Subscriber)public class IntSubscriber implements Subscriber<Integer> {

Subscription subscription;

int req = 1; // AtomicIntegerにすべき

@Override public void onSubscribe(Subscription s) {

subscription = s; s.request(req);

}

@Override public void onNext(Integer integer) {
System.out.println("Next -> " + integer);

if (--req == 0) {

req = new Random().nextInt(10) + 1;

subscription.request(req);
}

}

@Override public void onError(Throwable t) { /**/ }

@Override public void onComplete() {
System.out.println("Complete!");

}

}

使用例
Publisher<Integer> publisher = new IntPublisher();
Subscriber<Integer> subscriber = new IntSubscriber();
publisher.subscribe(subscriber);
// Next -> 1
// Next -> 2
// Next -> 3
// Next -> 4
// Next -> 5
// Next -> 6
// Next -> 7
// Next -> 8
// Next -> 9
// Next -> 10
// Complete!
使用例
Publisher<Integer> publisher = new IntPublisher();
Subscriber<Integer> subscriber = new IntSubscriber();
publisher.subscribe(subscriber);
// Next -> 1
// Next -> 2
// Next -> 3
// Next -> 4
// Next -> 5
// Next -> 6
// Next -> 7
// Next -> 8
// Next -> 9
// Next -> 10
// Complete!
IntPublisher/IntSubscriberは
インターフェースを実装しているだけ
で、実は仕様を満たしていない
(TCKを通らない)
Hot / Cold
• Hot Stream
• ずっと続いていて途中から見はじめるもの
• 株価情報、タイムライン、マウスイベント、タイマー
イベント、WebSocket、Server-Sent Events、メッ
セージキュー
• Cold Stream
• 再現可能で、最初から最後まで見れるもの
• 配列・リスト、ファイル、データベース、計算結果
(Future)
Reactive Streamsの仕様
• Subscriberは非同期でも同期でも
よい。ただし、ノンブロッキングで
ないといけない
実装プロダクト(フレームワーク)
• Reactor (Pivotal)
• RxJava (Netflix)
• Akka Streams (Typesafe)
• Vert.x (Redhat)
• Ratpack
• Quasar
http://www.reactive-streams.org/announce-1.0.0
実装データアクセスライブラリ
• MongoDB (公式)
• Apache Kafka
• Rabbit MQ (Scalaのみ)
• Slick3 (Scala)
• CouchDB (RxJava only)
• PostgreSQL (RxJava only、開発中?)
• Spark (検討中? SPARK-10420)
• など
MongoDB
• http://mongodb.github.io/mongo-java-driver-reactivestreams/
• MongoDB公式でReactive Streams API
がサポートされている
• チュートリアルがあるのでわかりやすい
MongoDB
MongoClient client = MongoClients.create(

"mongodb://localhost");

MongoDatabase db = client.getDatabase("mydb");

MongoCollection<Document> collection =
db.getCollection("test");
// Insert
Document doc = new Document("message", "Hello!");

Publisher<Success> insert = collection.insertOne(doc);

insert.subscribe(new PrintSubscriber<>("result=%s"));
// Select
Publisher<Document> docs = collection.find();

docs.subscribe(new PrintDocumentSubscriber());
MongoDB
// Bulk Operation
PrintSubscriber<BulkWriteResult> subscriber
= new PrintSubscriber<>("Bulk write results: %s");

collection.bulkWrite(Arrays.asList(

new InsertOneModel<>(new Document("_id", 4)),

new InsertOneModel<>(new Document("_id", 5)),

new InsertOneModel<>(new Document("_id", 6)),

new UpdateOneModel<>(new Document("_id", 1),
new Document("$set", new Document("x", 2))),

new DeleteOneModel<>(new Document("_id", 2)),

new ReplaceOneModel<>(new Document("_id", 3),
new Document("_id", 3).append("x",4))))
.subscribe(subscriber);
Apache Kafka
• https://github.com/softwaremill/reactive-kafka
• Scalaで実装されている。Java用API
も用意されている。
Apache Kafka
ReactiveKafka kafka = new ReactiveKafka();

ActorSystem system =
ActorSystem.create("ReactiveKafka");
Publisher<MessageAndMetadata<byte[], String>> topicA
= kafka.consume(new PropertiesBuilder.Consumer(
broker, zk, "topicA","group", decoder)
.build(), system);
topicA.subscribe(subscriber);
PostgreSQL
• https://github.com/alaisi/postgres-async-
driver
• https://github.com/mauricio/postgresql-async
PostgreSQL
Db db = new ConnectionPoolBuilder()
.hostname("localhost").port(5432).database("demo")
.username("postgres").password("")
.poolSize(20).build();


// RxJava's Observable
Observable<Row> result =
db.queryRows("select id,value from demo");
result.subscribe(...);
// to Reactive Streams
Publisher<Row> publisher =
RxReactiveStreams.toPublisher(result);
publisher.subscribe(...);
Publisher単体だと使いにくい
• Publisherはただのコールバック
• map、flatMap、merge、filer、take、zip
といっった合成(compose)や変換(transform)
のためのAPIがない → コールバック地獄に
• Publisherはインターフェースでしかないので
実装ライブラリ側で便利なAPIを提供可能。
コールバック地獄
• もっと命令的に書きたい
• ComposableなAPIが欲しい
(Reactive Streamsの範疇外)
Reactive Extensions
Reactive Extensions(Rx)
• 元々はMicrosoftのでErik Meijerによっ
て.NET(LINQ)向け開発されたライブラリ
• Observerパターンでイベントストリームを扱う
• イベントストリームを操作(変換、フィルタ、合成
など)する豊富なAPI群
• 多言語対応(Java, JavaScript, .NET, Swiftなど)
http://reactivex.io/
map
flatMap
filter
take
merge
zip
combileLatest