Be Proactive, Not Reactive
Be Reactive!
Rob Bosman 21-02-2019
-
Responsive
-
Resilient
-
Elastic
-
Message Driven
class FetchJokeService {
private static final Logger LOG = LoggerFactory.getLogger(FetchJokeServiceJava.class);
private static final String API_URL = "http://api.icndb.com/jokes/random?limitTo=[explicit,nerdy]"; // Chuck Norris jokes
static String fetchJoke() {
LOG.debug("fetch joke");
try {
final HttpURLConnection connection = (HttpURLConnection) new URL(API_URL).openConnection();
try {
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), UTF_8))) {
return reader.lines().collect(joining());
} catch (IOException e) {
throw new RuntimeException("Error reading from InputStream", e);
}
} finally {
connection.disconnect();
LOG.debug("fetched joke");
}
} catch (MalformedURLException e) {
throw new RuntimeException("Error in URL " + API_URL, e);
} catch (IOException e) {
throw new RuntimeException("Error connecting to " + API_URL, e);
}
}
}IntelliJ IDEA
MongoDB
Don't block the main Thread


Multi Thread programming

before Java 5:
-
Hashtable
-
Semaphore
-
synchronized
-
volatile
-
Runnable
-
ThreadPool
-
ThreadLocal
-
InterruptedException
Multithread programming
since Java 5:
-
ConcurrentHashMap
-
fork / join
-
Future



private fun findTheAnswer(): Int {
log.debug("pondering...")
Thread.sleep(1000)
return 42
}
fun backToTheFuture() {
val executor = Executors.newFixedThreadPool(2)
val future = executor.submit(::findTheAnswer)
log.debug("are you done? - ${future.isDone}")
// do other stuff
val answer = future.get() // blocking wait
log.debug("ah, the answer is $answer.")
executor.shutdown()
}IntelliJ IDEA
fun backToTheCompletableFuture() {
val provideAnswerCF = CompletableFuture
.supplyAsync {
log.debug("determine required processing time")
1500L
}
.thenApply { delayMillis ->
log.debug("pondering...")
Thread.sleep(delayMillis)
42
}
.thenApply { answer ->
log.debug("got it!")
answer
}
log.debug("do you know the answer? - ${provideAnswerCF.isDone}")
log.debug("ah, the answer is ${provideAnswerCF.get()}.") // blocking wait
}Two distinct 'flows':
-
preparation
-
execution
fun backToTheCompletableFuture() {
val provideAnswerCF = CompletableFuture
.supplyAsync {
log.debug("determine required processing time")
1500L
}
.thenApply { delayMillis ->
log.debug("pondering...")
Thread.sleep(delayMillis)
42
}
.thenApply { answer ->
log.debug("got it!")
answer
}
log.debug("do you know the answer? - ${provideAnswerCF.isDone}")
log.debug("ah, the answer is ${provideAnswerCF.get()}.") // blocking wait
}Functor
- filter
- transform (map)
Monad
-
is a Functor
-
flatten (flatMap)
A monad is just a monoid in the category of endofunctors, what's the problem?
Golem



Rabbi Jehoeda Löw




Functor
F(f\circ g)=F(f)\circ F(g)=F(g)\circ F(f)
F:M_{C}(x,y)\rightarrow M_{D}(F(x),F(y))
(G\circ F)(f)=G(F(f))
F:Obj(C)\rightarrow Obj(D)

Monads in Java 8
- Stream
- Optional
- CompletableFuture
- Rx Observable

ReactiveX Observable
-
lazy execution
-
push vs pull
-
hot vs cold
-
rich API
-
polyglot
-
well documented
-
version 1.0 vs 2.0
IntelliJ IDEA
fun run() {
log.debug("here we go")
val processControl = Semaphore(0)
val jokeRawO = Observable
.create<String> { emitter -> emitter.onNext(fetchJoke()) }
.subscribeOn(Schedulers.io())
Observable
.create<MongoClient> { emitter -> emitter.onNext(getMongoClient()) }
.subscribeOn(Schedulers.io())
.zipWith(jokeRawO,
BiFunction { mongoClient: MongoClient, jokeRaw: String ->
val joke = convertAndStore(jokeRaw, mongoClient)
mongoClient.close()
log.debug("closed MongoDB client")
joke
})
.subscribe { joke ->
log.info("'$joke'")
processControl.release()
}
log.debug("wait until all is done")
processControl.tryAcquire(3000, MILLISECONDS)
log.debug("there you are!")
}fun backToResilience() {
log.debug("here we go")
val processControl = Semaphore(0)
Single
.create<String> { emitter -> emitter.onSuccess(fetchJokeFlaky()) }
.timeout(200, MILLISECONDS)
.subscribeOn(Schedulers.io())
.doOnSuccess { jokeRaw -> if (!jokeRaw.contains("success"))
throw Throwable("invalid data: $jokeRaw") }
.doOnError { t -> log.warn("error detected: '${t.message}'", t) }
.retry(3)
.onErrorResumeNext(Single.just("fallback joke"))
.doFinally { processControl.release() }
.subscribe(
{ jokeRaw -> log.info("'$jokeRaw'") },
{ t -> log.error("an ERROR occurred", t) }
)
log.debug("wait a second...")
processControl.tryAcquire(1000, MILLISECONDS)
log.debug("there you are!")
}Software engineer
-
highly skilled craftsman
-
uses advanced tools
-
creative
-
bug fixer
-
about estimates:
"You can't rush art."
Single instead of Observable?
fun backToResilience() {
log.debug("here we go")
val processControl = Semaphore(0)
Single
.create<String> { emitter -> emitter.onSuccess(fetchJokeFlaky()) }
.timeout(200, MILLISECONDS)
.subscribeOn(Schedulers.io())
.doOnSuccess { jokeRaw -> if (!jokeRaw.contains("success"))
throw Throwable("invalid data: $jokeRaw") }
.doOnError { t -> log.warn("error detected: '${t.message}'", t) }
.retry(3)
.onErrorResumeNext(Single.just("fallback joke"))
.doFinally { processControl.release() }
.subscribe(
{ jokeRaw -> log.info("'$jokeRaw'") },
{ t -> log.error("an ERROR occurred", t) }
)
log.debug("wait a second...")
processControl.tryAcquire(1000, MILLISECONDS)
log.debug("there you are!")
}Recall two distinct phases:
-
preparation
-
execution
-
RxJava
-
Vert.x
-
Project Reactor
-
Java 9 Flow API
Resilience
dealing with errors

Separate (async) flows for
-
messages
-
Exceptions



goto(label);throw new Exception();fun backToResilience() {
log.debug("here we go")
val processControl = Semaphore(0)
Single
.create<String> { emitter -> emitter.onSuccess(fetchJokeFlaky()) }
.timeout(200, MILLISECONDS)
.subscribeOn(Schedulers.io())
.doOnSuccess { jokeRaw -> if (!jokeRaw.contains("success"))
throw Throwable("invalid data: $jokeRaw") }
.doOnError { t -> log.warn("error detected: '${t.message}'", t) }
.retry(3)
.onErrorResumeNext(Single.just("fallback joke"))
.doFinally { processControl.release() }
.subscribe(
{ jokeRaw -> log.info("'$jokeRaw'") },
{ t -> log.error("an ERROR occurred", t) }
)
log.debug("wait a second...")
processControl.tryAcquire(1000, MILLISECONDS)
log.debug("there you are!")
}Message Driven



request - reply (sync)
request - reply (async)
Vert.x
-
library
-
message driven
-
Verticles
-
non-blocking
-
integrates with Rx
-
rich API
-
polyglot
-
well documented

Vert.x
fun main() {
val vertx = Vertx.vertx()
CompositeFuture
.all(
CompositeFuture.all(
deployVerticle(vertx, PeanutPooper::class.java.name),
deployVerticle(vertx, Chocolatifier::class.java.name),
deployVerticle(vertx, Painter::class.java.name),
deployVerticle(vertx, LetterStamper::class.java.name),
deployVerticle(vertx, MnMPackager::class.java.name)),
CompositeFuture.all(
deployVerticle(vertx, PeanutSpeedLogger::class.java.name),
deployVerticle(vertx, HttpEventServer::class.java.name))
)
.setHandler { result ->
if (result.succeeded()) {
log.info("We have hyperdrive, captain.")
} else {
log.error("Error", result.cause())
}
}
}demo time!
Chocolate factory






One more thing...
Kotlin coroutines and Java fibers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.concurrent.thread
GitHub: Cerios/
ReactiveChocolate

Be Reactive!
By Rob Bosman
Be Reactive!
- 140