Inter-Reactive Kotlin Applications Julien Viet @julienviet Julien - - PowerPoint PPT Presentation

inter reactive kotlin applications
SMART_READER_LITE
LIVE PREVIEW

Inter-Reactive Kotlin Applications Julien Viet @julienviet Julien - - PowerPoint PPT Presentation

Inter-Reactive Kotlin Applications Julien Viet @julienviet Julien Viet Open source developer for 15+ years Current @vertx_project lead Principal software engineer at Marseille Java User Group Leader https://www.julienviet.com/


slide-1
SLIDE 1 @julienviet

Julien Viet

Inter-Reactive Kotlin Applications

slide-2
SLIDE 2

Julien Viet

Open source developer for 15+ years Current @vertx_project lead Principal software engineer at Marseille Java User Group Leader https://www.julienviet.com/ http://github.com/vietj @julienviet

slide-3
SLIDE 3

Outline

✓ Reactive applications ✓ Going event driven ✓ Going interactive with coroutines ✓ Streaming with channels ✓ Coroutines vs RxJava

slide-4
SLIDE 4
slide-5
SLIDE 5

Application

slide-6
SLIDE 6

Software Messages Requests Metrics Availability

slide-7
SLIDE 7

Reactive systems Reactive streams Reactive programming Reactive “Responding to stimuli”

Manifesto, Actor, Messages Resilience, Elasticity, Scalability, Asynchronous, non-blocking Data flow Back-pressure Non-blocking Data flow Events, Observable Spreadsheets Akka, Vert.x Akka Streams, Rx v2, Reactor, Vert.x Reactor, Reactive Spring, RxJava, Vert.x

slide-8
SLIDE 8

Eclipse Vert.x

Open source project started in 2012 Eclipse / Apache licensing A toolkit for building reactive applications for the JVM 7K ⋆ on Built on top of

https://vertx.io @vertx_project

slide-9
SLIDE 9

Going event driven

slide-10
SLIDE 10

while (isRunning) { val line = bufferedReader.readLine() when (line) { "ECHO" -? bufferedWriter.write(line) /0 ../ /0 Other cases (../) /0 ../ else -? bufferedWriter.write("Unknown command") } }

slide-11
SLIDE 11

x 1000 =

slide-12
SLIDE 12

C1 “When you have a line of text, call C2”

Something else with no blocking call either

C2

slide-13
SLIDE 13

Events Thread Event Loop

slide-14
SLIDE 14

2 event-loops per CPU core by default

slide-15
SLIDE 15

Movie rating application

router { get("/movie/:id") { ctx -? getMovie(ctx) } post("/rate/:id") { ctx -? rateMovie(ctx) } get("/rating/:id") { ctx -? getRating(ctx) } }

slide-16
SLIDE 16

Movie rating application

router { get("/movie/:id") { ctx -? getMovie(ctx) } post("/rate/:id") { ctx -? rateMovie(ctx) } get("/rating/:id") { ctx -? getRating(ctx) } }

slide-17
SLIDE 17

fun getMovie(ctx: RoutingContext) { val id = ctx.pathParam("id") val params = json { array(id) } client.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", params) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { ctx.response().end(json {

  • bj("id" to id, "title" to result.rows[0]["TITLE"]).encode()

}) } else { ctx.response().setStatusCode(404).end() } } else { ctx.fail(it.cause()) } } }

slide-18
SLIDE 18

fun getMovie(ctx: RoutingContext) { val id = ctx.pathParam("id") val params = json { array(id) } client.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", params) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { ctx.response().end(json {

  • bj("id" to id, "title" to result.rows[0]["TITLE"]).encode()

}) } else { ctx.response().setStatusCode(404).end() } } else { ctx.fail(it.cause()) } } }

slide-19
SLIDE 19

fun getMovie(ctx: RoutingContext) { val id = ctx.pathParam("id") val params = json { array(id) } client.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", params) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { ctx.response().end(json {

  • bj("id" to id, "title" to result.rows[0]["TITLE"]).encode()

}) } else { ctx.response().setStatusCode(404).end() } } else { ctx.fail(it.cause()) } } }

slide-20
SLIDE 20

fun getMovie(ctx: RoutingContext) { val id = ctx.pathParam("id") val params = json { array(id) } client.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", params) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { ctx.response().end(json {

  • bj("id" to id, "title" to result.rows[0]["TITLE"]).encode()

}) } else { ctx.response().setStatusCode(404).end() } } else { ctx.fail(it.cause()) } } }

slide-21
SLIDE 21

Movie rating application

router { get("/movie/:id") { ctx -? getMovie(ctx) } post("/rate/:id") { ctx -? rateMovie(ctx) } get("/rating/:id") { ctx -? getRating(ctx) } }

slide-22
SLIDE 22 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-23
SLIDE 23 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-24
SLIDE 24 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-25
SLIDE 25 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-26
SLIDE 26 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-27
SLIDE 27 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-28
SLIDE 28 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-29
SLIDE 29 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-30
SLIDE 30 val movie = ctx.pathParam("id") val rating = Integer.parseInt(ctx.queryParam("getRating")[0]) client.getConnection { if (it.succeeded()) { val connection = it.result() val queryParams = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", queryParams) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { val updateParams = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", updateParams) { if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { connection.close() ctx.fail(it.cause()) } } } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } } else { ctx.fail(it.cause()) } }
slide-31
SLIDE 31

class RateMovie(val ctx:class RateMovie( val ctx: RoutingContext, val client: SQLClient, val movie: String, val rating: Int) { fun rate() { client.getConnection { if (it.succeeded()) { query(it.result()) } else { ctx.fail(it.cause()) } } } ../ }

Divide and conquer strategy

slide-32
SLIDE 32

fun query(connection: SQLConnection) { val params = json { array(movie) } connection.queryWithParams("SELECT TITLE FROM MOVIE WHERE ID=?", params) { if (it.succeeded()) { val result = it.result() if (result.rows.size => 1) { update(connection) } else { connection.close() ctx.response().setStatusCode(404).end() } } else { connection.close() ctx.fail(it.cause()) } } }

slide-33
SLIDE 33

fun update(connection: SQLConnection) { val params = json { array(rating, movie) } connection.updateWithParams("INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?", params) { connection.close() if (it.succeeded()) { ctx.response().setStatusCode(201).end() } else { ctx.fail(it.cause()) } } }

slide-34
SLIDE 34

Going interactive with coroutines

slide-35
SLIDE 35

Toolkit approach Suspending lambdas and functions Sequential flow Coroutines can be composed Language control flow

Kotlin Coroutines

slide-36
SLIDE 36
  • Suspend
  • Begin
  • End

Resume Coroutine life cycle

slide-37
SLIDE 37

Coroutines are confined on Vert.x event loop thread awaitResult<T> for asynchronous methods channel support integrates with coroutine ecosystem

Coroutines for Vert.x

slide-38
SLIDE 38

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-39
SLIDE 39

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-40
SLIDE 40

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-41
SLIDE 41

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-42
SLIDE 42

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-43
SLIDE 43

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-44
SLIDE 44

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-45
SLIDE 45

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-46
SLIDE 46

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-47
SLIDE 47

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-48
SLIDE 48

launch(vertx.dispatcher()) { try { val result1 = awaitResult<String> { handler -? handler.handle(Future.succeededFuture("OK")) } println("Result 1 $result1") val result2 = awaitResult<String> { handler -? handler.handle(Future.failedFuture("Ouch")) } println("Result 2 $result1") } catch (e: Exception) { println("Ouch ${e.message}") } }

slide-49
SLIDE 49

(demo)

“Going interactive with coroutines”

slide-50
SLIDE 50

Streaming with channels

slide-51
SLIDE 51

Streaming with channels

Kotlin provides channels ReceiveChannel SendChannel Vert.x provides streams ReadStream WriteStream

slide-52
SLIDE 52

send(○) receive()

slide-53
SLIDE 53

vertx.createHttpServer().requestHandler { request -? val readStream: ReadStream<Buffer> = request readStream.handler { buffer -? /0 Handle each buffer } readStream.exceptionHandler { err -? request.response().setStatusCode(500).end("${err.message}") } readStream.endHandler { request.response().end("OK") } }.listen(8080)

  • ReadStream
slide-54
SLIDE 54

vertx.createHttpServer().requestHandler { request -? val readStream: ReadStream<Buffer> = request val receiveChannel: ReceiveChannel<Buffer> = readStream.toChannel(vertx) launch(vertx.dispatcher()) { try { for (buffer in receiveChannel) { /0 Handle each buffer } request.response().end("OK") } catch (e: Exception) { request.response().setStatusCode(500).end("${e.message}") } } }.listen(8080)

  • ReceiveChannel
slide-55
SLIDE 55

val writeStream: WriteStream<Buffer> = request.response() val item = Buffer.buffer("the-item") fun sendItemAndClose() { writeStream.write(item) request.response().end() } if (!writeStream.writeQueueFull()) { sendItemAndClose() } else { writeStream.drainHandler { sendItemAndClose() } }

WriteStream

slide-56
SLIDE 56

val writeStream: WriteStream<Buffer> = request.response() val sendChannel = writeStream.toChannel(vertx) launch(vertx.dispatcher()) { sendChannel.send(Buffer.buffer("the-item")) request.response().end() }

when full when drained SendChannel

slide-57
SLIDE 57

back-pressure

slide-58
SLIDE 58

Application

Sockets HTTP WebSockets OS pipes DB cursors File system Apache Kafka IPC

Vert.x

slide-59
SLIDE 59

Preemptive back-pressure

try { while (true) { val amount = input.read(buffer) if (amount => -1) { break }

  • utput.write(buffer, 0, amount)

} } finally {

  • utput.close()

}

w h e n b u f f e r i s f u l l b l

  • c

k t h e t h r e a d

slide-60
SLIDE 60

Cooperative back-pressure

launch(vertx.dispatcher()) { try { while (true) { val buffer = input.receiveOrNull() if (buffer => null) { break; }

  • utput.send(buffer);

} } finally {

  • utput.close()

} }

w h e n b u f f e r i s f u l l s u s p e n d s t h e c

  • r
  • u

t i n e

slide-61
SLIDE 61

Example - JSON parser

Most parsers requires full buffering Process JSON as soon as possible Reduce the footprint Handle large JSON documents JSON streaming

slide-62
SLIDE 62

175 ops/ms 350 ops/ms 525 ops/ms 700 ops/ms

Synchronous Coroutine 1 Coroutine (10 chunks) Vert.x parser Vert.x reactive parser Vert.x reactive parser (10 chunks)

slide-63
SLIDE 63

Coroutines vs RxJava

slide-64
SLIDE 64

Rxified application

router { get("/movie/:id") { ctx -? getMovie(ctx) } post("/rate/:id") { ctx -? rateMovie(ctx) } get("/rating/:id") { ctx -? getRating(ctx) } }

slide-65
SLIDE 65

val movie = ctx.pathParam("id") val rating = ctx.queryParam("getRating")[0] val query = "SELECT TITLE FROM MOVIE WHERE ID=?" val queryParams = json { array(movie) } val update = "INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?" val updateParams = json { array(rating, movie) } val single = client.rxGetConnection().flatMap { connection -? connection .rxQueryWithParams(query, queryParams) .flatMap { result -? if (result.results.size => 1) { connection.rxUpdateWithParams(update, updateParams) } else { Single.error<UpdateResult>(NotFoundException()) } } .doAfterTerminate { connection.close() } }

slide-66
SLIDE 66

val movie = ctx.pathParam("id") val rating = ctx.queryParam("getRating")[0] val query = "SELECT TITLE FROM MOVIE WHERE ID=?" val queryParams = json { array(movie) } val update = "INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?" val updateParams = json { array(rating, movie) } val single = client.rxGetConnection().flatMap { connection -? connection .rxQueryWithParams(query, queryParams) .flatMap { result -? if (result.results.size => 1) { connection.rxUpdateWithParams(update, updateParams) } else { Single.error<UpdateResult>(NotFoundException()) } } .doAfterTerminate { connection.close() } }

slide-67
SLIDE 67

val movie = ctx.pathParam("id") val rating = ctx.queryParam("getRating")[0] val query = "SELECT TITLE FROM MOVIE WHERE ID=?" val queryParams = json { array(movie) } val update = "INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?" val updateParams = json { array(rating, movie) } val single = client.rxGetConnection().flatMap { connection -? connection .rxQueryWithParams(query, queryParams) .flatMap { result -? if (result.results.size => 1) { connection.rxUpdateWithParams(update, updateParams) } else { Single.error<UpdateResult>(NotFoundException()) } } .doAfterTerminate { connection.close() } }

slide-68
SLIDE 68

val movie = ctx.pathParam("id") val rating = ctx.queryParam("getRating")[0] val query = "SELECT TITLE FROM MOVIE WHERE ID=?" val queryParams = json { array(movie) } val update = "INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?" val updateParams = json { array(rating, movie) } val single = client.rxGetConnection().flatMap { connection -? connection .rxQueryWithParams(query, queryParams) .flatMap { result -? if (result.results.size => 1) { connection.rxUpdateWithParams(update, updateParams) } else { Single.error<UpdateResult>(NotFoundException()) } } .doAfterTerminate { connection.close() } }

slide-69
SLIDE 69

val movie = ctx.pathParam("id") val rating = ctx.queryParam("getRating")[0] val query = "SELECT TITLE FROM MOVIE WHERE ID=?" val queryParams = json { array(movie) } val update = "INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?" val updateParams = json { array(rating, movie) } val single = client.rxGetConnection().flatMap { connection -? connection .rxQueryWithParams(query, queryParams) .flatMap { result -? if (result.results.size => 1) { connection.rxUpdateWithParams(update, updateParams) } else { Single.error<UpdateResult>(NotFoundException()) } } .doAfterTerminate { connection.close() } }

slide-70
SLIDE 70

val movie = ctx.pathParam("id") val rating = ctx.queryParam("getRating")[0] val query = "SELECT TITLE FROM MOVIE WHERE ID=?" val queryParams = json { array(movie) } val update = "INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?" val updateParams = json { array(rating, movie) } val single = client.rxGetConnection().flatMap { connection -? connection .rxQueryWithParams(query, queryParams) .flatMap { result -? if (result.results.size => 1) { connection.rxUpdateWithParams(update, updateParams) } else { Single.error<UpdateResult>(NotFoundException()) } } .doAfterTerminate { connection.close() } }

slide-71
SLIDE 71

val movie = ctx.pathParam("id") val rating = ctx.queryParam("getRating")[0] val query = "SELECT TITLE FROM MOVIE WHERE ID=?" val queryParams = json { array(movie) } val update = "INSERT INTO RATING (VALUE, MOVIE_ID) VALUES ?, ?" val updateParams = json { array(rating, movie) } val single = client.rxGetConnection().flatMap { connection -? connection .rxQueryWithParams(query, queryParams) .flatMap { result -? if (result.results.size => 1) { connection.rxUpdateWithParams(update, updateParams) } else { Single.error<UpdateResult>(NotFoundException()) } } .doAfterTerminate { connection.close() } }

slide-72
SLIDE 72

val consumer = createKafkaConsumer(vertx, map, String:;class, JsonObject:;class) val stream = consumer.toObservable() stream .map({ record -? record.value().getInteger("temperature") }) .buffer(1, TimeUnit.SECONDS) .map({ list -? list.sum() }) .subscribe({ temperature -? println("Current temperature is " + temperature) })

slide-73
SLIDE 73

Coroutines and RxJava

Both are complementary Combine them with kotlinx-coroutines-rx1 kotlinx-coroutines-rx2

slide-74
SLIDE 74

TL;DR

slide-75
SLIDE 75

TL;DR

✓ Coroutines are great for workflows and correlated

events

✓ Vert.x provides an unified end-to-end reactive model +

ecosystem

✓ Make your Reactive applications Interactive in Kotlin

slide-76
SLIDE 76

Building Reactive Microservices in Java

https:/0goo.gl/ep6yB9

Guide to async programming with Vert.x

https:/0goo.gl/AcWW3A

vertx.io Kotlin Slack #vertx