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
buffer
RxJava
• Netflix社が開発したRxのJava版
• Observable/Observer
• Observableに対して豊富なOperation APIを提供
Observable.just('a', 'b', 'c')
.take(2)
.map(Character::toUpperCase)
.consume(System.out::print); // AB
RxJava
• バージョン1.0ではReactive Streamsをnativeサポー
トしていない。Publisher <-> Observableアダプ
ターが必要
• バージョン2.0でReactive Streams対応予定
// Publisher <-> Observable
Publisher<String> publisher =
RxReactiveStreams.toPublisher(observable);
Observable<String> observable =
RxReactiveStreams.toObservable(publisher);
Reactor
• Pivotal社が開発
• LMAX Disruptorの高性能なRing Bufferを使用している
• Spring Frameworkの一部の機能で使用されている
• Reactive Streamsの

Publisher + Composable API → reactor.rx.Stream
Streams.just('a', 'b', 'c')
.take(2)
.map(Character::toUpperCase)
.consume(System.out::print); // AB
Reactor Projectshttp://projectreactor.io/docs/reference/#_about_the_project
Reactive StreamsとReactor Streamsの関係
BroadCaster
Action
Stream
Processor
Subscriber Publisher
Consumer
Class
Interface
extends implements
Reactive Streams
Reactor
Promise
Reactor Streamsへの変換
import reactor.rx.Stream;
import reactor.rx.Streams;
// Streamの要素を直接指定(Cold)
Stream<String> stream = Streams.just("a", "b");
// T[]やIterable<T>からStream<T>への変換(Cold)
Stream<String> stream = Streams.from(value);
// Publisher<T>からStream<T>へ変換
Stream<String> stream = Streams.wrap(publisher);
// Subscription.request(Long.MAX_VALUE)相当
// consumeに渡すラムダ式がonNextで実行される
stream.consume(s -> System.out.println(s));
// Subscription.request(2)を繰り返す
stream.capacity(2)
.consume(s -> System.out.println(s));
// デバッグログ出力
stream.log()
.consume(s -> System.out.println(s));
Reactive Streamsの仕様を
簡単に満たすヘルパー
List<Integer> items = /* ... */;
Publisher<Integer> publisher =
PublisherFactory.forEach((subscriber) -> {
Iterator<Integer> iterator
= subscriber.context();
if (iterator.hasNext()) {
subscriber.onNext(iterator.next());
} else {
subscriber.onComplete();
}
}, subscriber -> items.iterator());
reactor.rx.Streamを使うことで
Reactive Streamsを
Reactive Extensionsで操作できる
Kafka + Reactorで足し算
Publisher<MessageAndMetadata<byte[], String>> topicA =
kafka.consume(new PropertiesBuilder.Consumer(
broker, zk, "a", "group", decoder)

.build(), system);

Publisher<MessageAndMetadata<byte[], String>> topicB =
kafka.consume(new PropertiesBuilder.Consumer(
broker, zk, "b", "group", decoder)

.build(), system);


Publisher<Integer> a = Streams.wrap(topicA)
.map(x -> Integer.valueOf(x.message()));

Publisher<Integer> bx2 = Streams.wrap(topicB)
.map(x -> Integer.valueOf(x.message()))
.map(x -> x * 2);


Streams.combineLatest(a, bx2, tpl -> tpl.getT1()+tpl.getT2())

.consume(c -> {

System.out.println("a + b * 2 = " + c);

});
Kafka + Reactorで足し算
$ echo '3' | kafkacat -P -t a -b localhost:9092
$ echo '3' | kafkacat -P -t b -b localhost:9092
$ echo '1' | kafkacat -P -t b -b localhost:9092
a + b * 2 = 5
a + b * 2 = 9
Reactorでコナミコマンド
// Create a "Hot" stream
Broadcaster<String> stream
= Broadcaster.create(Environment.get());
stream.filter(x -> x.length() == 1)
.buffer(10, 1) // 10イベント分を1つずらしでまとめる
.map(s ->
"↑".equals(s.get(0)) && "↑".equals(s.get(1)) &&
""".equals(s.get(2)) && """.equals(s.get(3)) &&
"←".equals(s.get(4)) && "#".equals(s.get(5)) &&
"←".equals(s.get(6)) && "#".equals(s.get(7)) &&
"A".equals(s.get(8)) && "B".equals(s.get(9)))

.consume(isKonami -> {

System.out.println(isKonami ? "Konami!" : "NG");

});
Reactorでコナミコマンド
stream.onNext("A");

stream.onNext("↑");

stream.onNext("↑");

stream.onNext(""");

stream.onNext(""");

stream.onNext("←");

stream.onNext("#");

stream.onNext("←");

stream.onNext("#");

stream.onNext("A");

stream.onNext("B");

stream.onNext("←");
NG
Konami!
NG
コナミコマンド
HotStreamをエミュレート
外部サービス呼び出し (Blocking)
public interface LinkedInService {
List<String> findUsers(String keyword);
LinkedInProfile getConnection(String id);
}
public interface TwitterService {
TwitterProfile getUserProfile(String id);
}
public interface FacebookService {
FacebookProfile getUserProfile(String id);
}
外部サービス呼び出し (NonBlocking)
public interface LinkedInService {
Stream<String> findUsers(String keyword);
Stream<LinkedInProfile> getConnection(String id);
}
public interface TwitterService {
Stream<TwitterProfile> getUserProfile(String id);
}
public interface FacebookService {
Stream<FacebookProfile> getUserProfile(String id);
}
flatMap
linkedInService.findUsers("JJUG")
.take(5)
.flatMap(userName ->

linkedInService.getConnections(userName)
.take(3)
.flatMap(connection -> {

String twitterId = connection.getTwitterId();

Publisher<TwitterProfile> twitterProfile =
twitterService.getUserProfile(twitterId);

String facebookId = connection.getFacebookId();

Publisher<FacebookProfile> facebookProfile =
facebookService.getUserProfile(facebookId);

return Streams.zip(twitterProfile, facebookProfile,
(tp, fp) -> new UserConnectionInfo(
userName, connection, tp, fp));

})
)
.subscribe(System.out::println);
http://www.slideshare.net/SpringCentral/introduction-to-reactive-programming/60
Reactor or RxJava ?
Reactor RxJava
• Reactive Streamsを
Nativeサポート
• Spring連携
• Reactive Extensions
のサブセット
• Reactive Exensionsを
フルセットでサポート
• 他言語対応
• Reactive Streamsはア
ダプター経由
Reactive Streamsのメリット
• 非同期サポートライブラリを作りやすい
Reactive Streamsのメリット
• 非同期サポートライブラリを作りやすい
• async-db-driver
• Publisher/Subscriberのみを使ったコアライブラリ
• async-db-driver-reactor
• ReactorのStreamにラップ
• async-db-driver-rxjava
• RxJavaのObservableにラップ
• ...
https://github.com/ReactiveX/RxJava/wiki/Reactive-Streams#recommended-approach
package com.example.db.driver;
import org.reactivestreams.Publisher;
public class Database {
public Publisher getValue(String key) { /* ... */ }
}
package com.example.db.driver.reactor;
public class Database {
public Stream getValue(String key) {
return Streams.wrap(coreDatabase.getValue(key));
}
}
package com.example.db.driver.rxjava;
public class Database {
public Observable getValue(String key) {
return RxReactiveStreams.toObservable(
coreDatabase.getValue(key));
}
}
RxJava用
Reactor用
Core API
すべてがPublisherで繋がる
Repository
DBService
ControllerPublisher Publisher Publisher
PublisherPublisherPublisher
すべてがPublisherで繋がる
Repository
DBService
ControllerPublisher Publisher Publisher
PublisherPublisherPublisher
すべてがPublisherで繋がる
Repository
DBService
ControllerPublisher Publisher Publisher
PublisherPublisherPublisher
ここ
は?
Spring 5
Spring 5
• Q4 2016
• JDK 9サポート
• Java 8がベースライン
• HTTP 2対応
• Reactive対応
• JUnit5対応
Spring 5
• Q4 2016
• JDK 9サポート
• Java 8がベースライン
• HTTP 2対応
• Reactive対応
• JUnit5対応
Java Webアプリケーション
のReactive対応?
• Servlet APIは至るところでBlocking
• InputStream/OutputStream
• getParameters/getParts
• sendError
• ...
昨今のNoBlocking対応
フレームワーク
• Play Framework
• Vert.x
• Ratpack
• Reactor Net
• RxNetty
脱Servlet API組
↓
Netty利用
Servletはダメな子なのか
Servletはダメな子なのか
• Servlet 3.0 … 非同期処理サポート
Servletはダメな子なのか
• Servlet 3.0 … 非同期処理サポート
• Servlet 3.1 … Non-Blocking APIサ
ポート
Servlet 3.0
非同期処理サポート
void doGet(req, res) {
OutputStream out = res.getOutputStream();
AsyncContext ctx = req.startAsync();
doAsyncREST(req).thenAccept(json -> {
out.write(json);
ctx.complete();
});
}
http://www.slideshare.net/SimoneBordet/servlet-31-async-io
Servlet 3.0
非同期処理サポート
void doGet(req, res) {
OutputStream out = res.getOutputStream();
AsyncContext ctx = req.startAsync();
doAsyncREST(req).thenAccept(json -> {
out.write(json); // Blocking!!
ctx.complete();
});
}
http://www.slideshare.net/SimoneBordet/servlet-31-async-io
Servlet 3.1
Non-Blocking APIサポート
• リクエストの読み込み/レスポンスの書き込みを
Non-Blockingに行うためのAPIが追加された
• 以下のシーンで効果的
• ファイルアップロード/ダウンロード
• 低速ネットワークからの大量アクセス(モバイル、IoT)
void doGet(req, res) {
OutputStream out = res.getOutputStream();
AsyncContext ctx = req.startAsync();
out.setWriteListener(new WriteListener() {
void onWritePossible() {
while (on.isReady()) {
// ...
ctx.complete();
}
}
});
}
http://www.slideshare.net/SimoneBordet/servlet-31-async-io
Hello Worldサーブレット
@WebServlet(urlPatterns = "/hello"
, asyncSupported = true)
public class HelloServlet extends HttpServlet {
@Override
public void service(HttpServletRequest req,
HttpServletResponse resp) {
AsyncContext ctx = req.startAsync(req, resp);
ServletInputStream input = req.getInputStream();
ReadListener readListener =
new ReadListenerImpl(input, resp, ctx);
input.setReadListener(readListener);
}
class ReadListenerImpl implements ReadListener {
// ...
private final StringBuilder sb = new StringBuilder();
public ReadListenerImpl(ServletInputStream input,
HttpServletResponse resp, AsyncContext ctx) {/* ... */}
public void onDataAvailable() throws IOException {
int len = 0; byte b[] = new byte[1024];
while (input.isReady() && !input.isFinished()
&& (len = input.read(b)) != -1) {
sb.append(new String(b, 0, len));
}
}
public void onAllDataRead() throws IOException {
ServletOutputStream output = resp.getOutputStream();
WriteListener writeListener =
new WriteListenerImpl(output, ctx);
output.setWriteListener(writeListener);
}
public void onError(Throwable th) {
ctx.complete(); th.printStackTrace();
}
}
class WriteListenerImpl implements WriteListener {
private final ServletOutputStream output;
private final AsyncContext ctx;
public WriteListenerImpl(ServletOutputStream output,
AsyncContext ctx) {
this.output = output;
this.ctx = ctx;
}
public void onWritePossible() throws IOException {
output.print("Hello World!");
output.flush();
ctx.complete();
}
public void onError(Throwable th) {
th.printStackTrace();
}
}
Block
Servlet
Servlet
ReadListener
WriteListener
Servlet
ReadListener
WriteListener
書くのが辛い
• Non-Blockingの方が5倍コード量が多い
• isReady, isFinishedの制御が難しい
• ついgetParameterするとBlock...
http://www.slideshare.net/SimoneBordet/servlet-31-async-io
Spring Reactive
• Spring 5のReactive対応向け実験プロジェクト
• 開発メンバー
• Rossen Stoyanchev (Spring MVC)
• Stephane Maldini (Reactor, Reactive Streams)
• Arjen Poutsma (MVCのREST対応, Spring WS)
• Sébastien Deleuze (MVCのScriptEngine対応)
• Brian Clozel (MVCの静的リソースハンドリング対応)
https://github.com/spring-projects/spring-reactive
Servlet31HttpHandlerAdapter
• HttpServlet実装
• ReadListener/WriteListenerを
Observerパターンで実装
• Reactive Streams(Publisher/
Subscriber)を使用
• ReadListener→Publisher
• WriteListener→Subscriber
Servlet31HttpHandlerAdapter
https://github.com/spring-projects/spring-reactive/tree/master/src/main/java/org/springframework/http/server/servlet31
Instead of ...
interface ServerHttpRequest extends HttpRequest {
InputStream getBody();
}
interface ServerHttpResponse extends HttpMessage {
OutputStream getBody();
}
Use Publisher
interface ReactiveServerHttpRequest extends HttpRequest {
Publisher<ByteBuffer> getBody();
}
interface ReactiveServerHttpResponse extends HttpMessage {
Publisher<Void> setBody(Publisher<ByteBuffer> pub);
}
Spring MVCのアーキテクチャ
Dispatcher
Servlet
<<IF>>
HandlerAdapter
<<IF>>
HandlerMapping
Handler
(Object)
フレームワーク アプリケーション
org.springframework.web.servletパッケージ
これまでの
Spring MVCのアーキテクチャ
Dispatcher
Servlet
RequestMapping
HandlerAdapter
RequestMapping
HandlerMapping
@RequestMapping
HttpMessageConverter
org.springframework.web.servlet.mvc.method.annotationパッケージ
これまでの
Spring MVCのアーキテクチャ
Dispatcher
Servlet
RequestMapping
HandlerAdapter
RequestMapping
HandlerMapping
@RequestMapping
HttpMessageConverter
org.springframework.web.servlet.mvc.method.annotationパッケージ
所謂、
Controllerクラス
これまでの
Spring Reactiveのアーキテクチャ
<<IF>>
HandlerAdapter
<<IF>>
HandlerMapping
Handler
(Object)
フレームワーク アプリケーション
org.springframework.web.reactiveパッケージ
Servlet31Http
HandlerAdapter
<<IF>>
Reactive
HttpHandeler
Servlet31Http
HandlerAdapter
RequestMapping
HandlerAdapter
RequestMapping
HandlerMapping
@RequsetMapping
Encoder/Decoder
Spring Reactiveのアーキテクチャ
org.springframework.web.reactive.method.annotationパッケージ
Dispatcher
Handler
RequestMapping
HandlerAdapter
RequestMapping
HandlerMapping
@RequsetMapping
Encoder/Decoder
Spring Reactiveのアーキテクチャ
org.springframework.web.reactive.method.annotationパッケージ
Servlet31Http
HandlerAdapter
Dispatcher
Handler
Spring MVCと同じプログラミングモデルで
アプリケーションを記述できる
Spring Reactiveのアプローチ
Reactor Net
ReactorHttpHanderAdapter
RxNetty
RxNettyHttpHandlerAdapter
Undertow
UndertowHttpHandlerAdapter
Servlet31Http
HandlerAdapter
ReactiveStreams
@Controller
Jetty
Tomcat
Publisher
ReactiveHttpHandeler
Spring Reactiveのアプローチ
Reactor Net
ReactorHttpHanderAdapter
RxNetty
RxNettyHttpHandlerAdapter
Undertow
UndertowHttpHandlerAdapter
Servlet31Http
HandlerAdapter
ReactiveStreams
@Controller
Jetty
Tomcat
Publisher
ReactiveHttpHandeler
Servletコンテナ以外の
選択肢もある
Spring MVCのController
@RequestMapping("/")
@ResponseBody
Person hello(@RequestBody Person req) {
// ...
}
これまでの
Spring ReactiveのController
@RequestMapping("/")
@ResponseBody
Publisher<Person> hello(
@RequestBody Publisher<Person> req) {
// ...
}
Reactive Webアプリケーション
Repository
DBService
ControllerPublisher Publisher Publisher
PublisherPublisherPublisher
Reactive Webアプリケーション
Repository
DBService
ControllerPublisher Publisher Publisher
PublisherPublisherPublisher
SpringReactive
ReactiveExtensions
Reactive Streams
絶賛開発中
• Reactive Clientの実装
• Filter相当の実装
• Progressive HTML renderingの検討
Springは
Java EEの代替ではありません
落穂拾い
java.util.concurrent.Flow
• Reactive StreamsがJDK 9に入ります
• Flow.Publisher
• Flow.Subscriber
• Flow.Subscription
• Flow.Processor
• http://openjdk.java.net/jeps/266
• http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/Flow.html
@author Doug Lea
j.u.c.Flow <-> Reactive Streams
• ブリッジ作成を検討中
• https://github.com/reactive-streams/reactive-streams-jvm/issues/294
import java.util.concurrent.Flow;
import org.reactivestreams.Publisher;
// j.u.c.Flow -> Reactive Streams
Publisher<T> toReactive(Flow.Publisher<>T);
// Reactive Streams -> j.u.c.Flow
Flow.Publisher<T> toFlow(Publisher<T>);
Servlet 4
• HTTP2とReactive Streams (Back Pressure)の
連携案
• JDK9にj.u.c.Flowが入ったため、御蔵入りの模様
https://java.net/projects/servlet-spec/lists/jsr369-experts/archive/2015-08/message/1
まとめ
• Blocking ==💴な時代がきている
• Non-Blocking化を助けるReactiveなパラダイム
• 非同期ストリーム標準 Reactive Streams
• イベントストリームの変換・合成 Reactive
Extensions
• Reactive Webアプリケーション向けフレームワー
クSpring ReactiveがSpring 5に向けて開発中
まとめ
• Blocking ==💴な時代がきている
• Non-Blocking化を助けるReactiveなパラダイム
• 非同期ストリーム標準 Reactive Streams
• イベントストリームの変換・合成 Reactive
Extensions
• Reactive Webアプリケーション向けフレームワー
クSpring ReactiveがSpring 5に向けて開発中
Spring Framework
を使っていきましょう

Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3