Por: Wellington
Macedo
Introdução à
Spring Web Flux
Por: Wellington
Vulgo: Lelim, Oesli
ou Jão
● O que é Spring WebFlux?
● Reactive Programming
● Reactive Stream
○ Reactor
● Arquitetura do WebFlux
● Aplicação de Exemplo
● Non-blocking IO vs Blocking IO
● Event loop
● Vantagens
● Desvantagens
● Referências
Agenda
O que é Spring Web Flux
● É um projeto do Spring!
● Fornece suporte para programação reativa na camada Web do Spring
● Chegou na versão 5.0 do Spring (2017)
AH NÃO BRINCA WELLINGTON
😒
Reactive Programming
É um paradigma de programação
Reactive Programming
PUB/SUB
Reactive Programming
Vamos ver a aplicação rodando!
Reactive Programming
interface Consumer<T> {
fun consume(command: T)
}
Reactive Programming
class Publisher<T> {
private val consumers = mutableListOf<Consumer<T>>()
fun subscribe(consumer: Consumer<T>) {
consumers.add(consumer)
}
fun publish(value: T) = consumers.forEach { consumer -> consume.consume(value) }
}
Reactive Programming
class Publisher<T> {
private val consumers = mutableListOf<Consumer<T>>()
fun subscribe(consumer: Consumer<T>) {
consumers.add(consumer)
}
fun publish(value: T) = consumers.forEach { consumer -> consumer.consume(value) }
}
Reactive Programming
class Publisher<T> {
private val consumers = mutableListOf<Consumer<T>>()
fun subscribe(consumer: Consumer<T>) {
consumers.add(consumer)
}
fun publish(value: T) = consumers.forEach { consumer -> consumer.consume(value) }
}
Reactive Programming
class Publisher<T> {
private val consumers = mutableListOf<Consumer<T>>()
fun subscribe(consumer: Consumer<T>) {
consumers.add(consumer)
}
fun publish(value: T) = consumers.forEach { consumer -> consumer.consume(value) }
}
Reactive Programming
class CommandExecutor : Consumer<Int> {
override fun consume(command: Int) {
val resp = when (command) {
1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA"
2 -> "Figueira né feio"
3 -> throw FinishProgram()
else -> "Essa opção não existe feio"
}
println("Resposta: $resp")
}
}
Reactive Programming
class CommandExecutor : Consumer<Int> {
override fun consume(command: Int) {
val resp = when (command) {
1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA"
2 -> "Figueira né feio"
3 -> throw FinishProgram()
else -> "Essa opção não existe feio"
}
println("Resposta: $resp")
}
}
Reactive Programming
class CommandExecutor : Consumer<Int> {
override fun consume(command: Int) {
val resp = when (command) {
1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA"
2 -> "Figueira né feio"
3 -> throw FinishProgram()
else -> "Essa opção não existe feio"
}
println("Resposta: $resp")
}
}
Reactive Programming
class CommandExecutor : Consumer<Int> {
override fun consume(command: Int) {
val resp = when (command) {
1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA"
2 -> "Figueira né feio"
3 -> throw FinishProgram()
else -> "Essa opção não existe feio"
}
println("Resposta: $resp")
}
}
Reactive Programming
class CommandExecutor : Consumer<Int> {
override fun consume(command: Int) {
val resp = when (command) {
1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA"
2 -> "Figueira né feio"
3 -> throw FinishProgram()
else -> "Essa opção não existe feio"
}
println("Resposta: $resp")
}
}
Reactive Programming
class CommandLogger : Consumer<Int> {
private val logs = mutableListOf<String>()
override fun consume(command: Int) = log(command)
private fun log(command: Int) {
logs.add("[${now()}] command $command")
}
fun printLogs() = logs.forEach(::println)
}
Reactive Programming
class CommandLogger : Consumer<Int> {
private val logs = mutableListOf<String>()
override fun consume(command: Int) = log(command)
private fun log(command: Int) {
logs.add("[${now()}] command $command")
}
fun printLogs() = logs.forEach(::println)
}
Reactive Programming
class CommandLogger : Consumer<Int> {
private val logs = mutableListOf<String>()
override fun consume(command: Int) = log(command)
private fun log(command: Int) {
logs.add("[${now()}] command $command")
}
fun printLogs() = logs.forEach(::println)
}
Reactive Programming
class CommandLogger : Consumer<Int> {
private val logs = mutableListOf<String>()
override fun consume(command: Int) = log(command)
private fun log(command: Int) {
logs.add("[${now()}] command $command")
}
fun printLogs() = logs.forEach(::println)
}
Reactive Programming
class CommandLogger : Consumer<Int> {
private val logs = mutableListOf<String>()
override fun consume(command: Int) = log(command)
private fun log(command: Int) {
logs.add("[${now()}] command $command")
}
fun printLogs() = logs.forEach(::println)
}
Reactive Programming
class CommandLogger : Consumer<Int> {
private val logs = mutableListOf<String>()
override fun consume(command: Int) = log(command)
private fun log(command: Int) {
logs.add("[${now()}] command $command")
}
fun printLogs() = logs.forEach(::println)
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
fun main() {
val publisher = Publisher<Int>()
val commandLogger = CommandLogger()
val commandExecutor = CommandExecutor()
publisher.subscribe(commandLogger)
publisher.subscribe(commandExecutor)
try {
val read = Scanner(System.`in`)
while (true) {
menu()
print("Digite a opção desejada: ")
val command = read.nextInt()
publisher.publish(command)
}
} catch (e: FinishProgram) {
println("Comandos executados: ")
commandLogger.printLogs()
print("Encerrando o programa!")
}
}
Reactive Programming
Reagimos aos comandos publicados!
Reactive Programming
Zero acoplamento entre diferentes consumers!
Reactive Stream
É uma especificação!
● O que quer dizer especificação?
● Para o que serve essa especificação?
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactive Stream
Reactor
Publishers
Reactor
● Mono 0..1
0..infinito● Flux
Reactor
Exemplos
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.subscribe { value -> print(value) /* consumer */ }
// Imprime: Goku>Naruto
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.subscribe { value -> print(value) /* consumer */ }
// Imprime: Goku>Naruto
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.subscribe { value -> print(value) /* consumer */ }
// Imprime: Goku>Naruto
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.subscribe { value -> print(value) /* consumer */ }
// Imprime: Goku>Naruto
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.map(String::toUpperCase)
.map { value -> if(value == ">") "<" else value }
.subscribe { value -> print(value) /* consumer */ }
// Imprime: GOKU<NARUTO
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.map(String::toUpperCase)
.map { value -> if(value == ">") "<" else value }
.subscribe { value -> print(value) /* consumer */ }
// Imprime: GOKU<NARUTO
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.map(String::toUpperCase)
.map { value -> if(value == ">") "<" else value }
.subscribe { value -> print(value) /* consumer */ }
// Imprime: GOKU<NARUTO
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.map(String::toUpperCase)
.map { value -> if(value == ">") "<" else value }
.subscribe { value -> print(value) /* consumer */ }
// Imprime: GOKU<NARUTO
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.map(String::toUpperCase)
.map { value -> if(value == ">") "<" else value }
.subscribe { value -> print(value) /* consumer */ }
// Imprime: GOKU<NARUTO
Reactor
val publisher = Flux.fromArray(arrayOf("Goku", ">", "Naruto"))
publisher.map(String::toUpperCase)
.map { value -> if(value == ">") "<" else value }
.subscribe { value -> print(value) /* consumer */ }
// Imprime: GOKU<NARUTO
Reactor
val monoPublisher = Mono.just("Goku is the best!")
monoPublisher.subscribeOn(Schedulers.parallel())
.subscribe { print("[${Thread.currentThread().name}] $it") }
println("[${Thread.currentThread().name}] Who is the best?")
// Imprime: [main] Who is the best?
// [parallel-1] Goku is the best!
Reactor
val monoPublisher = Mono.just("Goku is the best!")
monoPublisher.subscribeOn(Schedulers.parallel())
.subscribe { print("[${Thread.currentThread().name}] $it") }
println("[${Thread.currentThread().name}] Who is the best?")
// Imprime: [main] Who is the best?
// [parallel-1] Goku is the best!
Reactor
val monoPublisher = Mono.just("Goku is the best!")
monoPublisher.subscribeOn(Schedulers.parallel())
.subscribe { print("[${Thread.currentThread().name}] $it") }
println("[${Thread.currentThread().name}] Who is the best?")
// Imprime: [main] Who is the best?
// [parallel-1] Goku is the best!
Reactor
val monoPublisher = Mono.just("Goku is the best!")
monoPublisher.subscribeOn(Schedulers.parallel())
.subscribe { print("[${Thread.currentThread().name}] $it") }
println("[${Thread.currentThread().name}] Who is the best?")
// Imprime: [main] Who is the best?
// [parallel-1] Goku is the best!
Reactor
val monoPublisher = Mono.just("Goku is the best!")
monoPublisher.subscribeOn(Schedulers.parallel())
.subscribe { print("[${Thread.currentThread().name}] $it") }
println("[${Thread.currentThread().name}] Who is the best?")
// Imprime: [main] Who is the best?
// [parallel-1] Goku is the best!
Reactor
val monoPublisher = Mono.just("Goku is the best!")
monoPublisher.subscribeOn(Schedulers.parallel())
.subscribe { print("[${Thread.currentThread().name}] $it") }
println("[${Thread.currentThread().name}] Who is the best?")
// Imprime: [main] Who is the best?
// [parallel-1] Goku is the best!
Arquitetura do Webflux
@Controller, @RequestMapping Router functions
spring-webflux
HTTP / Reactive Streams
Tomcat, Jetty, Netty,
Undertow
spring-webmvc
Servlet API
Servlet Container
Webflux
Talk is cheap, show me the code!
Blocking IO
Thread
Waiting
Running
Blocking IO
Thread
Waiting
Running
Blocking IO
Thread
Waiting
Running
Chamada ao
database
Blocking IO
IO Operation
Thread
Waiting
Running
Chamada ao
database
Blocking IO
IO Operation
Thread
Waiting
Running
Resposta do
database
Blocking IO
IO Operation
Thread
Waiting
Running
Resposta do
database
Non-blocking IO
Thread
Running
Kernel Process
Non-blocking IO
Thread
Running
Kernel Process
Non-blocking IO
Thread
Running
Kernel Process Chamada ao
database
Non-blocking IO
IO Operation
Thread
Running
Kernel Process
Thread sendo
aproveitada para
outros processos
Non-blocking IO
IO Operation
Thread
Running
Kernel Process Resposta do
database
Non-blocking IO
IO Operation
Thread
Running
Kernel Process
Event loop
É um design pattern!
Event loop
Event Queue
Event loop
Event Queue
Event loop
Event Loop
Thread
Event Queue
Event loop
Event Loop
Thread
Event Queue
Event loop
Event Loop
Thread
Event Queue
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
class EventLoop(private val eventQueue: BlockingQueue<Runnable>) {
init {
start()
}
private fun start() {
Thread(Runnable {
var task = eventQueue.take()
while (true) {
task.run()
task = eventQueue.take()
}
}).start()
}
fun put(task: Runnable) {
eventQueue.put(task)
}
}
Event loop
E se for necessário conectar com o DB ou ler um
arquivo, isso não vai bloquear o Event Loop?
Event loop
Event Loop
Kernel
Thread
Event Queue
Event loop
Event Loop
Non-blocking
ops
Kernel
Thread
Event Queue
Event loop
Event Loop
Non-blocking
ops
Kernel
Thread
Event Queue
put(event)
Event loop - NIO Libs
● R2DBC - Reactive Relational Database Connectivity
● Web Client
● Reactor Netty (UDP, TCP, HTTP)
Event loop
E se caso essa rotina for estritamente
bloqueante? Por exemplo, uma que use muito
CPU Bound. O que deve ser feito?
Event loop
Event Loop
Worker
Thread Pool
Event Queue
Thread
Event loop
Event Loop
Blocking Ops
Worker
Thread Pool
Event Queue
Thread
Event loop
Event Loop
Blocking Ops
Worker
Thread Pool
Event Queue
Thread
put(event)
Event Loop - CPU Bound exemplo
fun doHeavyProcess() {
println("running other spring boot application rs")
}
Event Loop - CPU Bound exemplo
@PostMapping("/heavy-process")
fun heavyProcess(): Mono<ResponseEntity<Nothing>> {
return Mono
.defer { Mono.justOrEmpty(::doHeavyProcess) }
.subscribeOn(Schedulers.parallel())
.map { ResponseEntity.ok().build<Nothing>() }
}
Event Loop - CPU Bound exemplo
@PostMapping("/heavy-process")
fun heavyProcess(): Mono<ResponseEntity<Nothing>> {
return Mono
.defer { Mono.justOrEmpty(::doHeavyProcess) }
.subscribeOn(Schedulers.parallel())
.map { ResponseEntity.ok().build<Nothing>() }
}
Event Loop - CPU Bound exemplo
@PostMapping("/heavy-process")
fun heavyProcess(): Mono<ResponseEntity<Nothing>> {
return Mono
.defer { Mono.justOrEmpty(::doHeavyProcess) }
.subscribeOn(Schedulers.parallel())
.map { ResponseEntity.ok().build<Nothing>() }
}
Event Loop - CPU Bound exemplo
@PostMapping("/heavy-process")
fun heavyProcess(): Mono<ResponseEntity<Nothing>> {
return Mono
.defer { Mono.justOrEmpty(::doHeavyProcess) }
.subscribeOn(Schedulers.parallel())
.map { ResponseEntity.ok().build<Nothing>() }
}
Vantagens e Desvantagens
Disclaimer
+ Utiliza melhor os recursos do hardware
+ Alta disponibilidade
Vantagens
- Codebase mais complexo
- É mais complexo para debugar
- Suposições erradas podem bloquear todo o servidor
- Não trabalha com thread local
- Logs
- Telemetria
- Stack Traces podem se tornar inúteis
Desvantagens
Referências
● https://medium.com/@rarepopa_68087/reactive-programming-with-spring-boot-and-webflux-734086f8c8a5
● https://tanzu.vmware.com/content/slides/reactive-applications-on-apache-tomcat-and-servlet-3-1-containers
● https://blog.codecentric.de/en/2019/04/explain-non-blocking-i-o-like-im-five/
● https://www.youtube.com/watch?v=2oXqbLhMS_A
● https://medium.com/ing-blog/how-does-non-blocking-io-work-under-the-hood-6299d2953c74
● https://www.scnsoft.com/blog/java-reactive-programming
● https://www.slideshare.net/kslisenko/networking-in-java-with-nio-and-netty-76583794
● https://www.reactivemanifesto.org/glossary#Non-Blocking
● https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web-reactive.html
● https://dzone.com/articles/spring-webflux-eventloop-vs-thread-per-request-mod
● https://www.reactive-streams.org/
● https://projectreactor.io/docs/core/release/reference/
● http://tutorials.jenkov.com/netty/overview.html
Códigos de exemplo
https://github.com/wellington3110/webfluxpresentation
!!!
/wellington3110
/in/wgmacedo/
wellingtongustavomacedo@gmail.com
Wellington Gustavo Macedo

Introdução à Spring Web Flux

  • 1.
    Por: Wellington Macedo Introdução à SpringWeb Flux Por: Wellington Vulgo: Lelim, Oesli ou Jão
  • 2.
    ● O queé Spring WebFlux? ● Reactive Programming ● Reactive Stream ○ Reactor ● Arquitetura do WebFlux ● Aplicação de Exemplo ● Non-blocking IO vs Blocking IO ● Event loop ● Vantagens ● Desvantagens ● Referências Agenda
  • 3.
    O que éSpring Web Flux ● É um projeto do Spring! ● Fornece suporte para programação reativa na camada Web do Spring ● Chegou na versão 5.0 do Spring (2017) AH NÃO BRINCA WELLINGTON 😒
  • 4.
    Reactive Programming É umparadigma de programação
  • 5.
  • 6.
    Reactive Programming Vamos vera aplicação rodando!
  • 7.
    Reactive Programming interface Consumer<T>{ fun consume(command: T) }
  • 8.
    Reactive Programming class Publisher<T>{ private val consumers = mutableListOf<Consumer<T>>() fun subscribe(consumer: Consumer<T>) { consumers.add(consumer) } fun publish(value: T) = consumers.forEach { consumer -> consume.consume(value) } }
  • 9.
    Reactive Programming class Publisher<T>{ private val consumers = mutableListOf<Consumer<T>>() fun subscribe(consumer: Consumer<T>) { consumers.add(consumer) } fun publish(value: T) = consumers.forEach { consumer -> consumer.consume(value) } }
  • 10.
    Reactive Programming class Publisher<T>{ private val consumers = mutableListOf<Consumer<T>>() fun subscribe(consumer: Consumer<T>) { consumers.add(consumer) } fun publish(value: T) = consumers.forEach { consumer -> consumer.consume(value) } }
  • 11.
    Reactive Programming class Publisher<T>{ private val consumers = mutableListOf<Consumer<T>>() fun subscribe(consumer: Consumer<T>) { consumers.add(consumer) } fun publish(value: T) = consumers.forEach { consumer -> consumer.consume(value) } }
  • 12.
    Reactive Programming class CommandExecutor: Consumer<Int> { override fun consume(command: Int) { val resp = when (command) { 1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA" 2 -> "Figueira né feio" 3 -> throw FinishProgram() else -> "Essa opção não existe feio" } println("Resposta: $resp") } }
  • 13.
    Reactive Programming class CommandExecutor: Consumer<Int> { override fun consume(command: Int) { val resp = when (command) { 1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA" 2 -> "Figueira né feio" 3 -> throw FinishProgram() else -> "Essa opção não existe feio" } println("Resposta: $resp") } }
  • 14.
    Reactive Programming class CommandExecutor: Consumer<Int> { override fun consume(command: Int) { val resp = when (command) { 1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA" 2 -> "Figueira né feio" 3 -> throw FinishProgram() else -> "Essa opção não existe feio" } println("Resposta: $resp") } }
  • 15.
    Reactive Programming class CommandExecutor: Consumer<Int> { override fun consume(command: Int) { val resp = when (command) { 1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA" 2 -> "Figueira né feio" 3 -> throw FinishProgram() else -> "Essa opção não existe feio" } println("Resposta: $resp") } }
  • 16.
    Reactive Programming class CommandExecutor: Consumer<Int> { override fun consume(command: Int) { val resp = when (command) { 1 -> "Amaz... zueira é o Mercado Livre HAUAHUHUAAHUA" 2 -> "Figueira né feio" 3 -> throw FinishProgram() else -> "Essa opção não existe feio" } println("Resposta: $resp") } }
  • 17.
    Reactive Programming class CommandLogger: Consumer<Int> { private val logs = mutableListOf<String>() override fun consume(command: Int) = log(command) private fun log(command: Int) { logs.add("[${now()}] command $command") } fun printLogs() = logs.forEach(::println) }
  • 18.
    Reactive Programming class CommandLogger: Consumer<Int> { private val logs = mutableListOf<String>() override fun consume(command: Int) = log(command) private fun log(command: Int) { logs.add("[${now()}] command $command") } fun printLogs() = logs.forEach(::println) }
  • 19.
    Reactive Programming class CommandLogger: Consumer<Int> { private val logs = mutableListOf<String>() override fun consume(command: Int) = log(command) private fun log(command: Int) { logs.add("[${now()}] command $command") } fun printLogs() = logs.forEach(::println) }
  • 20.
    Reactive Programming class CommandLogger: Consumer<Int> { private val logs = mutableListOf<String>() override fun consume(command: Int) = log(command) private fun log(command: Int) { logs.add("[${now()}] command $command") } fun printLogs() = logs.forEach(::println) }
  • 21.
    Reactive Programming class CommandLogger: Consumer<Int> { private val logs = mutableListOf<String>() override fun consume(command: Int) = log(command) private fun log(command: Int) { logs.add("[${now()}] command $command") } fun printLogs() = logs.forEach(::println) }
  • 22.
    Reactive Programming class CommandLogger: Consumer<Int> { private val logs = mutableListOf<String>() override fun consume(command: Int) = log(command) private fun log(command: Int) { logs.add("[${now()}] command $command") } fun printLogs() = logs.forEach(::println) }
  • 23.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 24.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 25.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 26.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 27.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 28.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 29.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 30.
    Reactive Programming fun main(){ val publisher = Publisher<Int>() val commandLogger = CommandLogger() val commandExecutor = CommandExecutor() publisher.subscribe(commandLogger) publisher.subscribe(commandExecutor) try { val read = Scanner(System.`in`) while (true) { menu() print("Digite a opção desejada: ") val command = read.nextInt() publisher.publish(command) } } catch (e: FinishProgram) { println("Comandos executados: ") commandLogger.printLogs() print("Encerrando o programa!") } }
  • 31.
  • 32.
    Reactive Programming Zero acoplamentoentre diferentes consumers!
  • 33.
    Reactive Stream É umaespecificação!
  • 34.
    ● O quequer dizer especificação? ● Para o que serve essa especificação? Reactive Stream
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.subscribe { value -> print(value) /* consumer */ } // Imprime: Goku>Naruto
  • 44.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.subscribe { value -> print(value) /* consumer */ } // Imprime: Goku>Naruto
  • 45.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.subscribe { value -> print(value) /* consumer */ } // Imprime: Goku>Naruto
  • 46.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.subscribe { value -> print(value) /* consumer */ } // Imprime: Goku>Naruto
  • 47.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.map(String::toUpperCase) .map { value -> if(value == ">") "<" else value } .subscribe { value -> print(value) /* consumer */ } // Imprime: GOKU<NARUTO
  • 48.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.map(String::toUpperCase) .map { value -> if(value == ">") "<" else value } .subscribe { value -> print(value) /* consumer */ } // Imprime: GOKU<NARUTO
  • 49.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.map(String::toUpperCase) .map { value -> if(value == ">") "<" else value } .subscribe { value -> print(value) /* consumer */ } // Imprime: GOKU<NARUTO
  • 50.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.map(String::toUpperCase) .map { value -> if(value == ">") "<" else value } .subscribe { value -> print(value) /* consumer */ } // Imprime: GOKU<NARUTO
  • 51.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.map(String::toUpperCase) .map { value -> if(value == ">") "<" else value } .subscribe { value -> print(value) /* consumer */ } // Imprime: GOKU<NARUTO
  • 52.
    Reactor val publisher =Flux.fromArray(arrayOf("Goku", ">", "Naruto")) publisher.map(String::toUpperCase) .map { value -> if(value == ">") "<" else value } .subscribe { value -> print(value) /* consumer */ } // Imprime: GOKU<NARUTO
  • 53.
    Reactor val monoPublisher =Mono.just("Goku is the best!") monoPublisher.subscribeOn(Schedulers.parallel()) .subscribe { print("[${Thread.currentThread().name}] $it") } println("[${Thread.currentThread().name}] Who is the best?") // Imprime: [main] Who is the best? // [parallel-1] Goku is the best!
  • 54.
    Reactor val monoPublisher =Mono.just("Goku is the best!") monoPublisher.subscribeOn(Schedulers.parallel()) .subscribe { print("[${Thread.currentThread().name}] $it") } println("[${Thread.currentThread().name}] Who is the best?") // Imprime: [main] Who is the best? // [parallel-1] Goku is the best!
  • 55.
    Reactor val monoPublisher =Mono.just("Goku is the best!") monoPublisher.subscribeOn(Schedulers.parallel()) .subscribe { print("[${Thread.currentThread().name}] $it") } println("[${Thread.currentThread().name}] Who is the best?") // Imprime: [main] Who is the best? // [parallel-1] Goku is the best!
  • 56.
    Reactor val monoPublisher =Mono.just("Goku is the best!") monoPublisher.subscribeOn(Schedulers.parallel()) .subscribe { print("[${Thread.currentThread().name}] $it") } println("[${Thread.currentThread().name}] Who is the best?") // Imprime: [main] Who is the best? // [parallel-1] Goku is the best!
  • 57.
    Reactor val monoPublisher =Mono.just("Goku is the best!") monoPublisher.subscribeOn(Schedulers.parallel()) .subscribe { print("[${Thread.currentThread().name}] $it") } println("[${Thread.currentThread().name}] Who is the best?") // Imprime: [main] Who is the best? // [parallel-1] Goku is the best!
  • 58.
    Reactor val monoPublisher =Mono.just("Goku is the best!") monoPublisher.subscribeOn(Schedulers.parallel()) .subscribe { print("[${Thread.currentThread().name}] $it") } println("[${Thread.currentThread().name}] Who is the best?") // Imprime: [main] Who is the best? // [parallel-1] Goku is the best!
  • 59.
    Arquitetura do Webflux @Controller,@RequestMapping Router functions spring-webflux HTTP / Reactive Streams Tomcat, Jetty, Netty, Undertow spring-webmvc Servlet API Servlet Container
  • 60.
    Webflux Talk is cheap,show me the code!
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
    Non-blocking IO IO Operation Thread Running KernelProcess Thread sendo aproveitada para outros processos
  • 71.
  • 72.
  • 73.
    Event loop É umdesign pattern!
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 80.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 81.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 82.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 83.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 84.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 85.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 86.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 87.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 88.
    Event loop class EventLoop(privateval eventQueue: BlockingQueue<Runnable>) { init { start() } private fun start() { Thread(Runnable { var task = eventQueue.take() while (true) { task.run() task = eventQueue.take() } }).start() } fun put(task: Runnable) { eventQueue.put(task) } }
  • 89.
    Event loop E sefor necessário conectar com o DB ou ler um arquivo, isso não vai bloquear o Event Loop?
  • 90.
  • 91.
  • 92.
  • 93.
    Event loop -NIO Libs ● R2DBC - Reactive Relational Database Connectivity ● Web Client ● Reactor Netty (UDP, TCP, HTTP)
  • 94.
    Event loop E secaso essa rotina for estritamente bloqueante? Por exemplo, uma que use muito CPU Bound. O que deve ser feito?
  • 95.
    Event loop Event Loop Worker ThreadPool Event Queue Thread
  • 96.
    Event loop Event Loop BlockingOps Worker Thread Pool Event Queue Thread
  • 97.
    Event loop Event Loop BlockingOps Worker Thread Pool Event Queue Thread put(event)
  • 98.
    Event Loop -CPU Bound exemplo fun doHeavyProcess() { println("running other spring boot application rs") }
  • 99.
    Event Loop -CPU Bound exemplo @PostMapping("/heavy-process") fun heavyProcess(): Mono<ResponseEntity<Nothing>> { return Mono .defer { Mono.justOrEmpty(::doHeavyProcess) } .subscribeOn(Schedulers.parallel()) .map { ResponseEntity.ok().build<Nothing>() } }
  • 100.
    Event Loop -CPU Bound exemplo @PostMapping("/heavy-process") fun heavyProcess(): Mono<ResponseEntity<Nothing>> { return Mono .defer { Mono.justOrEmpty(::doHeavyProcess) } .subscribeOn(Schedulers.parallel()) .map { ResponseEntity.ok().build<Nothing>() } }
  • 101.
    Event Loop -CPU Bound exemplo @PostMapping("/heavy-process") fun heavyProcess(): Mono<ResponseEntity<Nothing>> { return Mono .defer { Mono.justOrEmpty(::doHeavyProcess) } .subscribeOn(Schedulers.parallel()) .map { ResponseEntity.ok().build<Nothing>() } }
  • 102.
    Event Loop -CPU Bound exemplo @PostMapping("/heavy-process") fun heavyProcess(): Mono<ResponseEntity<Nothing>> { return Mono .defer { Mono.justOrEmpty(::doHeavyProcess) } .subscribeOn(Schedulers.parallel()) .map { ResponseEntity.ok().build<Nothing>() } }
  • 103.
  • 104.
    + Utiliza melhoros recursos do hardware + Alta disponibilidade Vantagens
  • 105.
    - Codebase maiscomplexo - É mais complexo para debugar - Suposições erradas podem bloquear todo o servidor - Não trabalha com thread local - Logs - Telemetria - Stack Traces podem se tornar inúteis Desvantagens
  • 106.
    Referências ● https://medium.com/@rarepopa_68087/reactive-programming-with-spring-boot-and-webflux-734086f8c8a5 ● https://tanzu.vmware.com/content/slides/reactive-applications-on-apache-tomcat-and-servlet-3-1-containers ●https://blog.codecentric.de/en/2019/04/explain-non-blocking-i-o-like-im-five/ ● https://www.youtube.com/watch?v=2oXqbLhMS_A ● https://medium.com/ing-blog/how-does-non-blocking-io-work-under-the-hood-6299d2953c74 ● https://www.scnsoft.com/blog/java-reactive-programming ● https://www.slideshare.net/kslisenko/networking-in-java-with-nio-and-netty-76583794 ● https://www.reactivemanifesto.org/glossary#Non-Blocking ● https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web-reactive.html ● https://dzone.com/articles/spring-webflux-eventloop-vs-thread-per-request-mod ● https://www.reactive-streams.org/ ● https://projectreactor.io/docs/core/release/reference/ ● http://tutorials.jenkov.com/netty/overview.html
  • 107.
  • 108.