Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

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

32.706 visualizaciones

Publicado el

Spring Reactiveについて

Publicado en: Tecnología
  • Sé el primero en comentar

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

  1. 1. Reactive Webアプリケーション - そしてSpring 5へ Toshiaki Maki (@making) JJUG CCC 2015 Fall 2015-11-30 #jjug_ccc #ccc_ef3
  2. 2. 聞いたことあります? • Reactive Programming? • Reactive Manifest? • Reactive Streams? • Reactive Extensions? • Reactive ... ?
  3. 3. • Reactiveが重要視されてきた背景 • Reactive Streams • Reactive Extensions • Spring 5 今日話すこと
  4. 4. Reactive Extensions(Rx) Reactive Programming Reactive Streams Reactor RxJava Spring 5 プロラミング パラダイム 仕様(?) 今日話すこと 実装 ライブラリ Spring Framework
  5. 5. 元ネタ http://www.slideshare.net/SpringCentral/reactive-web-applications-53170985 http://www.slideshare.net/SpringCentral/introduction-to-reactive-programming
  6. 6. Reactiveが重要視され てきた背景
  7. 7. Amdalの法則 https://en.wikipedia.org/wiki/Amdahl%27s_law
  8. 8. Amdalの法則 https://en.wikipedia.org/wiki/Amdahl%27s_law プログラムの並列化による速度の向上は、 プログラムの逐次処理部分によって制限される
  9. 9. Amdalの法則 https://en.wikipedia.org/wiki/Amdahl%27s_law プログラムの並列化による速度の向上は、 プログラムの逐次処理部分によって制限される プログラム中の 並列実行可能部分が50% だと、いくら並列度を増や しても2倍の性能向上し か見込めない
  10. 10. スケールアウト戦略 • メモリばかり消費してCPUに空き… ✦ 大量のスレッドプール ✦ブロッキングI/O
  11. 11. ブロッキングコードを 書いていては ハードにいくら金をかけても 性能向上に限界がある
  12. 12. Blocking is evil • ブロッキングコードは並列化の妨げ • 2大ブロッキング • リクエストの受信待ち/レスポンス の送信待ち • DBクエリの結果待ち
  13. 13. Blocking is evil • ブロッキングコードは並列化の妨げ • 2大ブロッキング • リクエストの受信待ち/レスポンス の送信待ち • DBクエリの結果待ち InputStream OutputStream JDBC
  14. 14. • IoT • マイクロサービス 今後のトレンド
  15. 15. • IoT • マイクロサービス 低速で膨大な リクエスト が予想される 今後のトレンド
  16. 16. • IoT • マイクロサービス 低速で膨大な リクエスト が予想される 今後のトレンド Blocking == 💴
  17. 17. Non-Blocking! • 命令型ではなくリスナー/コールバックス タイル • スレッド数を少なく • →省メモリ • →コンテキストスイッチを減らす • →CPUを有効活用
  18. 18. Reactiveプログラミング • 命令型と異なるプログラミングパラダイム • 連続的なデータをイベントハンドラで処理する • データフロー(データ同士の関係性)に着目した イベントドリブンなプログラミング
  19. 19. Reactiveプログラミング • 命令型と異なるプログラミングパラダイム • 連続的なデータをイベントハンドラで処理する • データフロー(データ同士の関係性)に着目した イベントドリブンなプログラミング ノンブロッキングコードを書く のに向いている
  20. 20. 最も身近なReactiveプログラミング?
  21. 21. 最も身近なReactiveプログラミング?
  22. 22. 最も身近なReactiveプログラミング?
  23. 23. 最も身近なReactiveプログラミング?
  24. 24. 最も身近なReactiveプログラミング? A, Bの変更に伴い Cも変わる
  25. 25. データフロー A B 2 + C A, Bの変更に伴い Cも変わるhttps://speakerdeck.com/okapies/reactive-streams-ru-men-number-jjug
  26. 26. イベントストリーム x2 + 1  2      4      3 9    6    8    6    5    9 8      4      2  64      2      1  3 A B C
  27. 27. イベントストリーム x2 + 1  2      4      3 9    6    8    6    5    9 8      4      2  64      2      1  3 A B C
  28. 28. イベントストリーム x2 + 1  2      4      3 9    6    8    6    5    9 8      4      2  64      2      1  3 A B C
  29. 29. イベントストリーム x2 + 1  2      4      3 9    6    8    6    5    9 8      4      2  64      2      1  3 A B C
  30. 30. イベントストリーム x2 + 1  2      4      3 9    6    8    6    5    9 8      4      2  64      2      1  3 A B C
  31. 31. イベントストリーム x2 + 1  2      4      3 9    6    8    6    5    9 8      4      2  64      2      1  3 A B C
  32. 32. イベントストリーム x2 + 1  2      4      3 9    6    8    6    5    9 8      4      2  64      2      1  3 A B C
  33. 33. 命令型 int A = 3; int B = 3; int C = A + B * 2; // 9 B = 1; A, Bが変更されても Cは変わらない
  34. 34. 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
  35. 35. A, Bの変更に伴い Cも変わる
  36. 36. Reactive Webアプリケーション Repository DBService Controller
  37. 37. Reactive Webアプリケーション Repository DBService Controller
  38. 38. Reactive Webアプリケーション Repository DBService Controller Blocking Blocking
  39. 39. Reactive Webアプリケーション Repository DBService Controller Blocking BlockingBlocking 他サービス呼び出し
  40. 40. Reactive Streams
  41. 41. Reactive Streams • http://www.reactive-streams.org/ • 非同期ストリームの標準仕様 • BackPressure付き
  42. 42. Reactive Streamsの動機 1. 技術面: データを送り側の勢いが受け 取り側の処理速度より速い場合に、デー タが れることを防ぎたい (BackPressure) 2. 開発面: 似たような機能(API)を持つ製 品群の相互互換性を持たせたい
  43. 43. 関わっている会社 • Typesafe • Pivotal • Netflix • RedHat • など
  44. 44. 関わっている会社 • Typesafe ... Akka Streams • Pivotal ... Reactor • Netflix ... RxJava • RedHat ... Vert.x • など
  45. 45. Reactive Streamsが提供するもの • 4つのインターフェース • 仕様ドキュメント • TCK (仕様をテストするJUnitの親クラス)
  46. 46. 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
  47. 47. 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
  48. 48. 呼び出し順 onSubscribe onNext* (onError | onComplete)?
  49. 49. Subscription SubscriberPublisher Application
  50. 50. Subscription subscribe(Subscriber) SubscriberPublisher Application
  51. 51. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription)
  52. 52. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1)
  53. 53. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●)
  54. 54. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2)
  55. 55. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●)
  56. 56. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●)
  57. 57. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●) request(4)
  58. 58. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●) request(4) onNext(●)
  59. 59. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●) request(4) onNext(●) onNext(●)
  60. 60. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●) request(4) onNext(●) onNext(●) onComplete()
  61. 61. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(Long.MAX_VALUE)
  62. 62. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(Long.MAX_VALUE) onNext(●)
  63. 63. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(Long.MAX_VALUE) onNext(●) onNext(●)
  64. 64. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(Long.MAX_VALUE) onNext(●) onNext(●) onNext(●)
  65. 65. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(Long.MAX_VALUE) onNext(●) onNext(●) onNext(●) onNext(●)
  66. 66. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(Long.MAX_VALUE) onNext(●) onNext(●) onNext(●) onNext(●) onNext(●)
  67. 67. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(Long.MAX_VALUE) onNext(●) onNext(●) onNext(●) onNext(●) onNext(●) onComplete()
  68. 68. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●) request(4)
  69. 69. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●) request(4)💣 💥
  70. 70. Subscription subscribe(Subscriber) SubscriberPublisher Application onSubscribe(Subscription) request(1) onNext(●) request(2) onNext(●) onNext(●) request(4) onError(Throwable) 💣 💥
  71. 71. RxJavaからinspired RxJava Reactive Streams Observable<T> Publisher<T> Observer<T> Subscriber<T> Subscription Subscription
  72. 72. 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)
  73. 73. 実装例 (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() {}
 });}}
  74. 74. 実装例 (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!");
 }
 }

  75. 75. 使用例 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!
  76. 76. 使用例 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を通らない)
  77. 77. Hot / Cold • Hot Stream • ずっと続いていて途中から見はじめるもの • 株価情報、タイムライン、マウスイベント、タイマー イベント、WebSocket、Server-Sent Events、メッ セージキュー • Cold Stream • 再現可能で、最初から最後まで見れるもの • 配列・リスト、ファイル、データベース、計算結果 (Future)
  78. 78. Reactive Streamsの仕様 • Subscriberは非同期でも同期でも よい。ただし、ノンブロッキングで ないといけない
  79. 79. 実装プロダクト(フレームワーク) • Reactor (Pivotal) • RxJava (Netflix) • Akka Streams (Typesafe) • Vert.x (Redhat) • Ratpack • Quasar http://www.reactive-streams.org/announce-1.0.0
  80. 80. 実装データアクセスライブラリ • MongoDB (公式) • Apache Kafka • Rabbit MQ (Scalaのみ) • Slick3 (Scala) • CouchDB (RxJava only) • PostgreSQL (RxJava only、開発中?) • Spark (検討中? SPARK-10420) • など
  81. 81. MongoDB • http://mongodb.github.io/mongo-java-driver-reactivestreams/ • MongoDB公式でReactive Streams API がサポートされている • チュートリアルがあるのでわかりやすい
  82. 82. 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());
  83. 83. 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);
  84. 84. Apache Kafka • https://github.com/softwaremill/reactive-kafka • Scalaで実装されている。Java用API も用意されている。
  85. 85. 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);
  86. 86. PostgreSQL • https://github.com/alaisi/postgres-async- driver • https://github.com/mauricio/postgresql-async
  87. 87. 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(...);
  88. 88. Publisher単体だと使いにくい • Publisherはただのコールバック • map、flatMap、merge、filer、take、zip といっった合成(compose)や変換(transform) のためのAPIがない → コールバック地獄に • Publisherはインターフェースでしかないので 実装ライブラリ側で便利なAPIを提供可能。
  89. 89. コールバック地獄 • もっと命令的に書きたい • ComposableなAPIが欲しい (Reactive Streamsの範疇外)
  90. 90. Reactive Extensions
  91. 91. Reactive Extensions(Rx) • 元々はMicrosoftのでErik Meijerによっ て.NET(LINQ)向け開発されたライブラリ • Observerパターンでイベントストリームを扱う • イベントストリームを操作(変換、フィルタ、合成 など)する豊富なAPI群 • 多言語対応(Java, JavaScript, .NET, Swiftなど) http://reactivex.io/
  92. 92. map
  93. 93. flatMap
  94. 94. filter
  95. 95. take
  96. 96. merge
  97. 97. zip
  98. 98. combileLatest
  99. 99. buffer
  100. 100. RxJava • Netflix社が開発したRxのJava版 • Observable/Observer • Observableに対して豊富なOperation APIを提供 Observable.just('a', 'b', 'c') .take(2) .map(Character::toUpperCase) .consume(System.out::print); // AB
  101. 101. 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);
  102. 102. 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
  103. 103. Reactor Projectshttp://projectreactor.io/docs/reference/#_about_the_project
  104. 104. Reactive StreamsとReactor Streamsの関係 BroadCaster Action Stream Processor Subscriber Publisher Consumer Class Interface extends implements Reactive Streams Reactor Promise
  105. 105. 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);
  106. 106. // 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));
  107. 107. 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());
  108. 108. reactor.rx.Streamを使うことで Reactive Streamsを Reactive Extensionsで操作できる
  109. 109. 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);
 });
  110. 110. 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
  111. 111. 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");
 });
  112. 112. 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をエミュレート
  113. 113. 外部サービス呼び出し (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); }
  114. 114. 外部サービス呼び出し (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); }
  115. 115. 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
  116. 116. Reactor or RxJava ? Reactor RxJava • Reactive Streamsを Nativeサポート • Spring連携 • Reactive Extensions のサブセット • Reactive Exensionsを フルセットでサポート • 他言語対応 • Reactive Streamsはア ダプター経由
  117. 117. Reactive Streamsのメリット • 非同期サポートライブラリを作りやすい
  118. 118. 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
  119. 119. 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
  120. 120. すべてがPublisherで繋がる Repository DBService ControllerPublisher Publisher Publisher PublisherPublisherPublisher
  121. 121. すべてがPublisherで繋がる Repository DBService ControllerPublisher Publisher Publisher PublisherPublisherPublisher
  122. 122. すべてがPublisherで繋がる Repository DBService ControllerPublisher Publisher Publisher PublisherPublisherPublisher ここ は?
  123. 123. Spring 5
  124. 124. Spring 5 • Q4 2016 • JDK 9サポート • Java 8がベースライン • HTTP 2対応 • Reactive対応 • JUnit5対応
  125. 125. Spring 5 • Q4 2016 • JDK 9サポート • Java 8がベースライン • HTTP 2対応 • Reactive対応 • JUnit5対応
  126. 126. Java Webアプリケーション のReactive対応? • Servlet APIは至るところでBlocking • InputStream/OutputStream • getParameters/getParts • sendError • ...
  127. 127. 昨今のNoBlocking対応 フレームワーク • Play Framework • Vert.x • Ratpack • Reactor Net • RxNetty 脱Servlet API組 ↓ Netty利用
  128. 128. Servletはダメな子なのか
  129. 129. Servletはダメな子なのか • Servlet 3.0 … 非同期処理サポート
  130. 130. Servletはダメな子なのか • Servlet 3.0 … 非同期処理サポート • Servlet 3.1 … Non-Blocking APIサ ポート
  131. 131. 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
  132. 132. 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
  133. 133. Servlet 3.1 Non-Blocking APIサポート • リクエストの読み込み/レスポンスの書き込みを Non-Blockingに行うためのAPIが追加された • 以下のシーンで効果的 • ファイルアップロード/ダウンロード • 低速ネットワークからの大量アクセス(モバイル、IoT)
  134. 134. 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
  135. 135. 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); }
  136. 136. 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(); } }
  137. 137. 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(); } }
  138. 138. Block Servlet
  139. 139. Servlet ReadListener WriteListener
  140. 140. Servlet ReadListener WriteListener
  141. 141. 書くのが辛い • Non-Blockingの方が5倍コード量が多い • isReady, isFinishedの制御が難しい • ついgetParameterするとBlock... http://www.slideshare.net/SimoneBordet/servlet-31-async-io
  142. 142. 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
  143. 143. Servlet31HttpHandlerAdapter • HttpServlet実装 • ReadListener/WriteListenerを Observerパターンで実装 • Reactive Streams(Publisher/ Subscriber)を使用
  144. 144. • ReadListener→Publisher • WriteListener→Subscriber Servlet31HttpHandlerAdapter https://github.com/spring-projects/spring-reactive/tree/master/src/main/java/org/springframework/http/server/servlet31
  145. 145. Instead of ... interface ServerHttpRequest extends HttpRequest { InputStream getBody(); } interface ServerHttpResponse extends HttpMessage { OutputStream getBody(); }
  146. 146. Use Publisher interface ReactiveServerHttpRequest extends HttpRequest { Publisher<ByteBuffer> getBody(); } interface ReactiveServerHttpResponse extends HttpMessage { Publisher<Void> setBody(Publisher<ByteBuffer> pub); }
  147. 147. Spring MVCのアーキテクチャ Dispatcher Servlet <<IF>> HandlerAdapter <<IF>> HandlerMapping Handler (Object) フレームワーク アプリケーション org.springframework.web.servletパッケージ これまでの
  148. 148. Spring MVCのアーキテクチャ Dispatcher Servlet RequestMapping HandlerAdapter RequestMapping HandlerMapping @RequestMapping HttpMessageConverter org.springframework.web.servlet.mvc.method.annotationパッケージ これまでの
  149. 149. Spring MVCのアーキテクチャ Dispatcher Servlet RequestMapping HandlerAdapter RequestMapping HandlerMapping @RequestMapping HttpMessageConverter org.springframework.web.servlet.mvc.method.annotationパッケージ 所謂、 Controllerクラス これまでの
  150. 150. Spring Reactiveのアーキテクチャ <<IF>> HandlerAdapter <<IF>> HandlerMapping Handler (Object) フレームワーク アプリケーション org.springframework.web.reactiveパッケージ Servlet31Http HandlerAdapter <<IF>> Reactive HttpHandeler
  151. 151. Servlet31Http HandlerAdapter RequestMapping HandlerAdapter RequestMapping HandlerMapping @RequsetMapping Encoder/Decoder Spring Reactiveのアーキテクチャ org.springframework.web.reactive.method.annotationパッケージ Dispatcher Handler
  152. 152. RequestMapping HandlerAdapter RequestMapping HandlerMapping @RequsetMapping Encoder/Decoder Spring Reactiveのアーキテクチャ org.springframework.web.reactive.method.annotationパッケージ Servlet31Http HandlerAdapter Dispatcher Handler Spring MVCと同じプログラミングモデルで アプリケーションを記述できる
  153. 153. Spring Reactiveのアプローチ Reactor Net ReactorHttpHanderAdapter RxNetty RxNettyHttpHandlerAdapter Undertow UndertowHttpHandlerAdapter Servlet31Http HandlerAdapter ReactiveStreams @Controller Jetty Tomcat Publisher ReactiveHttpHandeler
  154. 154. Spring Reactiveのアプローチ Reactor Net ReactorHttpHanderAdapter RxNetty RxNettyHttpHandlerAdapter Undertow UndertowHttpHandlerAdapter Servlet31Http HandlerAdapter ReactiveStreams @Controller Jetty Tomcat Publisher ReactiveHttpHandeler Servletコンテナ以外の 選択肢もある
  155. 155. Spring MVCのController @RequestMapping("/") @ResponseBody Person hello(@RequestBody Person req) { // ... } これまでの
  156. 156. Spring ReactiveのController @RequestMapping("/") @ResponseBody Publisher<Person> hello( @RequestBody Publisher<Person> req) { // ... }
  157. 157. Reactive Webアプリケーション Repository DBService ControllerPublisher Publisher Publisher PublisherPublisherPublisher
  158. 158. Reactive Webアプリケーション Repository DBService ControllerPublisher Publisher Publisher PublisherPublisherPublisher SpringReactive ReactiveExtensions Reactive Streams
  159. 159. 絶賛開発中 • Reactive Clientの実装 • Filter相当の実装 • Progressive HTML renderingの検討
  160. 160. Springは Java EEの代替ではありません
  161. 161. 落穂拾い
  162. 162. 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
  163. 163. 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>);
  164. 164. 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
  165. 165. まとめ • Blocking ==💴な時代がきている • Non-Blocking化を助けるReactiveなパラダイム • 非同期ストリーム標準 Reactive Streams • イベントストリームの変換・合成 Reactive Extensions • Reactive Webアプリケーション向けフレームワー クSpring ReactiveがSpring 5に向けて開発中
  166. 166. まとめ • Blocking ==💴な時代がきている • Non-Blocking化を助けるReactiveなパラダイム • 非同期ストリーム標準 Reactive Streams • イベントストリームの変換・合成 Reactive Extensions • Reactive Webアプリケーション向けフレームワー クSpring ReactiveがSpring 5に向けて開発中 Spring Framework を使っていきましょう

×