Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

JSUG勉強会 2018年その5 Spring I/O 報告会

837 Aufrufe

Veröffentlicht am

Spring I/O 2018 でお話があった以下のセッションについて自分なりにまとめてみました。
- Flight of the Flux
https://speakerdeck.com/simonbasle/flight-of-the-flux
- Under the hood of Reactive Data Access
https://speakerdeck.com/mp911de/under-the-hood-of-reactive-data-access-1
- REST beyond the obvious
https://speakerdeck.com/olivergierke/rest-beyond-the-obvious-api-design-for-ever-evolving-systems

Veröffentlicht in: Technologie
  • Als Erste(r) kommentieren

JSUG勉強会 2018年その5 Spring I/O 報告会

  1. 1. Spring I/O 2018 報告会 2018.7.30 Tagbangers 佐々⽊木 !1
  2. 2. ⾃自⼰己紹介 タグバンガーズ JSUGスタッフ 佐々⽊木亜⾥里里沙 TagBar !2
  3. 3. お詫び Reactive多めになってしまいました !3
  4. 4. Spring I/O 2018 Digest https://codezine.jp/article/detail/10930 !4
  5. 5. !5
  6. 6. ・Flight of the Flux: A look at Reactor execution model ・Under the hood of Reactive Data access ・REST beyond the obvious !6
  7. 7. https://www.slideshare.net/TakuyaIwatsuka/spring-5 http://otndnld.oracle.co.jp/ondemand/javaday2017/D1-D5_JavaDayTokyo2017 https://www.slideshare.net/mikeneck/jjug-ccc-2018-spring-i7-netty Thank you!! http://argius.hatenablog.jp/entry/20131226/1388068061 https://blog.ik.am/entries/413 https://qiita.com/kencharos/items/2ad090e7a6b7e9664f04 http://d.hatena.ne.jp/Kazuhira/20180107/1515327957 !7
  8. 8. サーバサイドの Reactive の話 イベント ≒ 通信 データが発⽣生するごとに どのようにデータを処理理していくかの ⼯工程をReadableに書ける + データもらう側-あげる側間で データ流量量調整できる ここでのReactive !8
  9. 9. マルチスレッド 別スレッドを⽴立てて処理理 -> 結果はCallback、 Future ⾮非同期処理理(async) 待たない ノンブロッキング I/O IOの準備ができているか⾃自分で⾒見見に⾏行行く できてない場合は制御を返す イベントループ(シングルスレッド)を使ってループ中に複数のIOの状態を監視 IO準備ができたものはループ中で処理理を⾏行行う Future, CompletableFuture NIO というかNetty 並⾏行行処理理(Concurrency) 複数の異異なるタスクを同時に実⾏行行状態にする 処理理実⾏行行中にキャンセルして処理理をやめたりなど。 java.util.concurrent 並列列処理理(Parallel) Java 1.5〜 Executor, Threadpool, ConcurrentHashMap … Java 1.5〜 データを分割(folk)して⼦子スレッドで並列列実⾏行行し、 終わったら⾃自分のスレッドも完了了させる Work-stealing (ForkJoinPool) joinによってタスクが中断してスレッドが別のforkしたタスクの処理理に回される Functional Interface Function Consumerlambda式でデータの加⼯工⼯工程をReadable&Composableな形で書ける Supplier Predicate Fork/Join Java 1.7〜 Java 1.8〜 Reactor を⽀支える技術要素 対応技術説明 Stream#parallel !9
  10. 10. Reactor Reactor Your app Reactor Micro service Driver Connector Spring Data Spring Cloud Spring WebFlux NIO NIO NIO Web Server UI Storage WebSocket Non-blocking end-to-end data flow architecture JVM Server/ Client Reactor Spring ※ mongodb redis couchbase RabbitMQ Kafka Event loop ※使ったり使わなかったり Event Driven Netty Spring Reactor Java !10
  11. 11. Flight of the Flux: A look at Reactor execution model Nothing happens until you subscribe  Assembly & Execution Time  Debug  Hot&Cold Publisher(例例外) Concurrency Agnosticだけどスレッドを使う  publishOn&subscribeOn  work stealing その他のよしなに系処理理  macro & micro fusion !11
  12. 12. Publisher データを送る⼈人 Subscriber データをもらう⼈人 Subscription pub/subの受付 おさらい:Pub/Sub onNext() onError|onComplete onSubscribe スタッフ(受付) おきゃく店の⼈人 こういうデータが流れてきたら こうやって処理理する そのデータください request(n) cancel() !12
  13. 13. subscribeするまではpipeline(⼯工程、ステップ) が書かれてるだけAssembly Time Nothing happens until you subscribe !13
  14. 14. subscribe()して初めてデータが流れ出す subscribeすると最後にfluxがnewされソースにシグナルを送り返す ソースがデータを渡し始め、パイプライン(⼯工程)のステップごとに 処理理されていく Execution Time Subscriber がデータをリクエストして受け取るの繰り返し Nothing happens until you subscribe Subscription RunTime !14
  15. 15. 例例 Flux.just("kaicho","maki", "toki", "iwatsuka", "ikeya") .filter(s -> s.startWith("i")) .map(s -> s.toUpperCase()) .take(2) .subscribe(System.out::println); !15
  16. 16. filter(->) map(->) take(n) Flux.just
  17. 17. filter(->) map(->) take(n) Publisher new Op new Op new Op new Flux.just !17 Assembly Time
  18. 18. filter(->) map(->) take(n) subscribe() Subscriber new Publisher new Op new Op new Op new Flux.just !18 Subscription Time
  19. 19. filter(->) map(->) take(n) subscribe() Subscriber new subscribe(->) subscribe(->) subscribe(->) Subscriber/Subscription new Subscriber/Subscription new Subscriber/Subscription new Publisher new Op new Op new Op new Flux.just subscribe(->) !19
  20. 20. filter(->) map(->) take(n) subscribe() Subscriber new subscribe(->) subscribe(->) subscribe(->) Subscriber/Subscription new Subscriber/Subscription new Subscriber/Subscription new Publisher new Op new Op new Op new Flux.just subscribe(->) Subscription new !20
  21. 21. filter(->) map(->) take(n) subscribe() Subscriber new subscribe(->) subscribe(->) subscribe(->) onSubscribe onSubscribe onSubscribe onSubscribe Subscriber/Subscription new Subscriber/Subscription new Subscriber/Subscription new Publisher new Op new Op new Op new Flux.just subscribe(->) Subscription new !21
  22. 22. filter(->) map(->) take(n) subscribe() Subscriber new subscribe(->) subscribe(->) subscribe(->) onSubscribe onSubscribe onSubscribe onSubscribe request(n) Subscriber/Subscription new Subscriber/Subscription new Subscriber/Subscription new Publisher new Op new Op new Op new Flux.just subscribe(->) request(n) request(n) request(n) Subscription new !22 データ
  23. 23. filter(->) map(->) take(n) subscribe() Subscriber new subscribe(->) subscribe(->) subscribe(->) onSubscribe onSubscribe onSubscribe onSubscribe request(n) Subscriber/Subscription new Subscriber/Subscription new Subscriber/Subscription new Publisher new Op new Op new Op new Flux.just onNext subscribe(->) request(n) request(n) request(n) onNext onNext onNext Subscription new !23 データ
  24. 24. filter(->) map(->) take(n) subscribe() Subscriber new subscribe(->) subscribe(->) subscribe(->) onSubscribe onSubscribe onSubscribe onSubscribe request(n) Subscriber/Subscription new Subscriber/Subscription new Subscriber/Subscription new Publisher new Op new Op new Op new Flux.just onNext subscribe(->) cancel request(n) request(n) request(n) onNext onNext onNext cancel cancel Subscription new !24 データ
  25. 25. filter(->) map(->) take(n) subscribe() Subscriber new subscribe(->) subscribe(->) subscribe(->) onSubscribe onSubscribe onSubscribe onSubscribe request(n) Subscriber/Subscription new Subscriber/Subscription new Subscriber/Subscription new Publisher new Op new Op new Op new Flux.just onNext subscribe(->) cancel onComplete request(n) request(n) request(n) onNext onNext onNext cancel cancel onComplete onComplete onComplete Subscription new !25 データ
  26. 26. Assembly Time / Execution Time はタイミングが違う java.lang.IndexOutOfBoundsException: Source emitted more than one item at reactor.core.publisher.MonoSingle$SingleSubscriber.onNext(MonoSingle.java:129) at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:445) at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:379) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121) at reactor.core.publisher.FluxRange$RangeSubscription.slowPath(FluxRange.java:154) at reactor.core.publisher.FluxRange$RangeSubscription.request(FluxRange.java:109) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162) at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:332) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java: 90) at reactor.core.publisher.FluxRange.subscribe(FluxRange.java:68) at reactor.core.publisher.FluxMapFuseable.subscribe(FluxMapFuseable.java:63) at reactor.core.publisher.FluxFlatMap.subscribe(FluxFlatMap.java:97) at reactor.core.publisher.MonoSingle.subscribe(MonoSingle.java:58) at reactor.core.publisher.Mono.subscribe(Mono.java:3096) at reactor.core.publisher.Mono.subscribeWith(Mono.java:3204) at reactor.core.publisher.Mono.subscribe(Mono.java:3090) at reactor.core.publisher.Mono.subscribe(Mono.java:3057) at reactor.core.publisher.Mono.subscribe(Mono.java:3029) at reactor.guide.GuideTests.debuggingCommonStacktrace(GuideTests.java:995) → stacktraceが追いづらい !26
  27. 27. Hooks.onOperatorDebug(); Assembly timeの機構を利利⽤用 アプリ起動時(Flux,Monoのインスタンス 化前)に⾏行行われる java.lang.IndexOutOfBoundsException: Source emitted more than one item at reactor.core.publisher.MonoSingle$SingleSubscriber.onNext(MonoSingle.java:120) at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:314) ... ... at reactor.core.publisher.Mono.subscribeWith(Mono.java:2668) at reactor.core.publisher.Mono.subscribe(Mono.java:2629) at reactor.core.publisher.Mono.subscribe(Mono.java:2604) at reactor.core.publisher.Mono.subscribe(Mono.java:2582) at reactor.guide.GuideTests.debuggingActivated(GuideTests.java:727) Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Assembly trace from producer [reactor.core.publisher.MonoSingle] : reactor.core.publisher.Flux.single(Flux.java:5335) reactor.guide.GuideTests.scatterAndGather(GuideTests.java:689) reactor.guide.GuideTests.populateDebug(GuideTests.java:702) org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55) org.junit.rules.RunRules.evaluate(RunRules.java:20) Error has been observed by the following operator(s): |_ Flux.single(TestWatcher.java:55) Debugモードにする魔法 エラーが起きるとFluxがnewされた箇所を Assembly Timeに解析した内容を元に 追記してくれる (がコストが⾼高い)!27
  28. 28. checkpoint() Debugの仕⽅方2 !28 public void checkpointWithDescriptionIsLight() { StringWriter sw = new StringWriter(); Flux<Integer> tested = Flux.range(1, 10) .map(i -> i < 3 ? i : null) .filter(i -> i % 2 == 0) .checkpoint("foo") .doOnError(t -> t.printStackTrace(new PrintWriter(sw))); StepVerifier.create(tested) .expectNext(2) .verifyError(); String debugStack = sw.toString(); } Assembly site of producer [reactor.core.publisher.FluxFilterFuseable] is identified by light checkpoint [foo].
  29. 29. Nothing happens until you subscribe… ですが 例例外あり Hot Publisher Cold Publisher これはこっちの話 subscriptionごとにデータを新しくこさえる 例例:HTTP request ⼀一度でもsubscribeされたら(⼀一度データが流れ出したら) その後はお構いなく流れていく 途中からsubscribeしたSubscriberはsubscribeするより前に 何かが起こっていることになる 例例:Processorクラスを継承したもの !29
  30. 30. Flux<String> source = Flux.fromIterable(Arrays.asList("blue", "green", "orange", "purple")) .map(String::toUpperCase); source.subscribe(d -> System.out.println("Subscriber 1 to Cold Source: "+d)); source.subscribe(d -> System.out.println("Subscriber 2 to Cold Source: "+d)); Cold Publisher !30
  31. 31. Flux<String> source = Flux.fromIterable(Arrays.asList("blue", "green", "orange", "purple")) .map(String::toUpperCase); source.subscribe(d -> System.out.println("Subscriber 1 to Cold Source: "+d)); source.subscribe(d -> System.out.println("Subscriber 2 to Cold Source: "+d)); Subscriber 1 to Cold Source: BLUE Subscriber 1 to Cold Source: GREEN Subscriber 1 to Cold Source: ORANGE Subscriber 1 to Cold Source: PURPLE Subscriber 2 to Cold Source: BLUE Subscriber 2 to Cold Source: GREEN Subscriber 2 to Cold Source: ORANGE Subscriber 2 to Cold Source: PURPLE Cold Publisher !31
  32. 32. UnicastProcessor<String> hotSource = UnicastProcessor.create(); Flux<String> hotFlux = hotSource.publish() .autoConnect() .map(String::toUpperCase); hotSource.onNext("blue"); hotSource.onNext("green"); hotFlux.subscribe(d -> System.out.println("Subscriber 1 to Hot Source: "+d)); hotFlux.subscribe(d -> System.out.println("Subscriber 2 to Hot Source: "+d)); hotSource.onNext("orange"); hotFlux.subscribe(d -> System.out.println("Subscriber 3 to Hot Source: "+d)); hotSource.onNext("purple"); hotSource.onComplete(); Hot Publisher !32
  33. 33. Subscriber 1 to Hot Source: BLUE Subscriber 1 to Hot Source: GREEN Subscriber 1 to Hot Source: ORANGE Subscriber 2 to Hot Source: ORANGE Subscriber 1 to Hot Source: PURPLE Subscriber 2 to Hot Source: PURPLE Subscriber 3 to Hot Source: PURPLE Hot Publisher UnicastProcessor<String> hotSource = UnicastProcessor.create(); Flux<String> hotFlux = hotSource.publish() .autoConnect() .map(String::toUpperCase); hotSource.onNext("blue"); hotSource.onNext("green"); hotFlux.subscribe(d -> System.out.println("Subscriber 1 to Hot Source: "+d)); hotFlux.subscribe(d -> System.out.println("Subscriber 2 to Hot Source: "+d)); hotSource.onNext("orange"); hotFlux.subscribe(d -> System.out.println("Subscriber 3 to Hot Source: "+d)); hotSource.onNext("purple"); hotSource.onComplete(); !33
  34. 34. Concurrency agnosticだがスレッドを⽴立てる subscribeOn(Schedulers) … subscriptionが発⽣生するシーケンスを変更更する publishOn(Schedulers) … subscribeしてスレッドが始まりパイプラインの先頭から 処理理されていくが、publishOnがきたら別のスレッドに ジャンプする Schedulers#immediate(),single(),elastic(),parallel() !34
  35. 35. !35
  36. 36. !36
  37. 37. !37
  38. 38. macro fusion … 同じオペレータをまとめる オペレータのインスタンス⽣生成を節約するFusion !38
  39. 39. micro fusion … 異異なるオペレータを1つのキューに(話し合って)まとめる QueueSubscription #requestFusion(requestMode) Fusesable.NONE Fusesable.SYNC Fusesable.ASYNC Fusesable.ANY Fusesable.THREAD_BARRIER !39
  40. 40. ・Flight of the Flux: A look at Reactor execution model ・Under the hood of Reactive Data access ・REST beyond the obvious !40
  41. 41. Under the hood of Reactive Database 話のスコープ 各DB Driverの実装の仕⽅方 !41
  42. 42. Reactor Reactor Your app Reactor Micro service Driver Connector Spring Data Spring Cloud Spring WebFlux NIO NIO NIO Web Server UI Storage WebSocket Non-blocking end-to-end data flow architecture JVM Server/ Client Reactor Spring ※ mongodb redis couchbase RabbitMQ Kafka Event loop ※使ったり使わなかったり Event Driven Netty Spring Reactor Java !42
  43. 43. !43
  44. 44. prefetch, backpressureのおかげで ReactiveDriverはデータがどこまで取得できているのかを把握できるため データをすべてロードするまで待たなくて良い !44
  45. 45. Spring Data で Reactive に DB アクセスできるもの •Spring Data Commons •Spring Data JPA •Spring Data KeyValue •Spring Data LDAP •Spring Data MongoDB •Spring Data REST •Spring Data Redis •Spring Data for Apache Cassandra •Spring Data for Apache Geode •Spring Data for Apache Solr •Spring Data for Pivotal GemFire •Spring Data Couchbase (community module) •Spring Data Elasticsearch (community module) •Spring Data Neo4j (community module) http://projects.spring.io/spring-data/ ドキュメント指向 Key-Value型 Column型 KVS !45 アクションログ
  46. 46. Spring Data JDBC JVM RDBS Reactor MongoDB Reactive Streams Java Driver JVM MongoDB Reactor Lettuce JVM Redis Reactor DataStax Java driver for Cassandra JVM Cassandra Reactor Reactive Couchbase JVM Couchbase Server Spring Data JPA Spring Data MongoDB Spring Data Redis Spring Data for Cassandra Spring Data Couchbase Under the hood of ~の話のスコープ … 各ベンダが⽤用意している DB Driver の Reactive 対応状況 !46
  47. 47. Spring Data JDBC JVM RDBS Reactor MongoDB Reactive Streams Java Driver JVM MongoDB Reactor Lettuce JVM Redis Reactor DataStax Java driver for Cassandra JVM Cassandra Reactor Reactive Couchbase JVM Couchbase Server Spring Data JPA Spring Data MongoDB Spring Data Redis Spring Data for Cassandra Spring Data Couchbase Under the hood of ~の話のスコープ … 各ベンダが⽤用意している DB Driver の Reactive 対応状況 Fully blocking API ADBA,R2DBCなどがnon-blocking api !47
  48. 48. •Driverが必要とするThreadの構造 •タイムアウトのハンドリング •ConnectTimeout •ReadTimeout •CommandTimeout ReactiveでないDBとの違い !48 Imperative Imperative Reactive Reactive ⾃自分で完了了シグナルを送る必要がある データが来るのを待ち続けるので ReadTimeoutの概念が存在しない
  49. 49. Reactive Drivers End-to-end nonblocking Asynchronous request-response processing Non-blocking pooling Non-blocking connect !49
  50. 50. mongodb dependencies mongodb の driver が Reactive Streamsの API を実装している mongodb Publisher を Flux.from(mongodbのPublisher) によって Reactor の Publisher に変換してる !50
  51. 51. mongodb dependencies mongodb の driver が Reactive Streamsの API を実装している mongodb Publisher を Flux.from(mongodbのPublisher) によって Reactor の Publisher に変換してる !51
  52. 52. redis dependencies !52
  53. 53. cassandra dependencies !53
  54. 54. cassandra dependencies !54
  55. 55. couchbase dependencies !55
  56. 56. couchbase dependencies !56
  57. 57. MongoDB Reactive-over-async driver (Reactor/RxJava) Cursor lifecycle Pluggable I/O … SSL通信、Java6を使⽤用する場合はNettyを使う必要あり Pooled Connections CursorにBatchsizeがある .filter()を使うとデータをrequestした分以上に取得してしまうので、ReactorのlimitRateを使う Redis Apache Cassandra Reactive Driver (Lettuce5) Netty&Project Reactor データがリクエストされるとすぐにデータがemitされる Pooled Connectionはオプション Reactive Driverは持っていない (Reactive bridge over async driver) Netty-based Driver(Datastax) Pooled Connection Couchbase Reactive Driver Netty&RxJava1 -> RxJava2に移⾏行行しつつある JavaのexecuteAsync() !57
  58. 58. !58
  59. 59. ・Flight of the Flux: A look at Reactor execution model ・Under the hood of Reactive Data access ・REST beyond the obvious !59
  60. 60. !60
  61. 61. ありがとうございました !61

×