Introduccion a Programacion Reactiva
-
Upload
andres-almiray -
Category
Technology
-
view
235 -
download
5
Transcript of Introduccion a Programacion Reactiva
@aalmiray
ESTADO Programación Imperativa (Procedural) var a := b + c
println a
Programación Declarativa (Funcional)
(println (sum(b, c))
@aalmiray
TIEMPO Programación Imperativa var a := b + c
println a
Programación Reactiva
var a := b + c
a.done( v -> println v)
@aalmiray
SINCRONO VS ASINCRONO Las operaciones síncronas bloquean el flujo hasta obtener un resultado (éxito o error). Las operaciones asíncronas no bloquean el flujo, permiten que el programa continúe. Resultados y/o errores son manipulados en un momento posterior, típicamente haciendo uso de funciones (callbacks).
@aalmiray
FUTUROS Y PROMESAS Describen un objecto que actúa como mediador sobre un valor que se desconoce inicialmente. El término Futuro y Promesa se usan indistintamente, pero existe una diferencia: • El Futuro es una referencia de sólo lectura al valor
esperado. • La promesa es un contenedor de escritura única,
encargado de definir el valor del Futuro. https://en.wikipedia.org/wiki/Futures_and_promises
@aalmiray
FUTUROS Y PROMESAS Los Futuros en Java son inherentemente síncronos, demonstrable por los siguientes métodos definidos en java.util.concurrent.Future
Vget()
Vget(longtimeout,TimeUnitunit)
@aalmiray
FUTUROS Y PROMESAS Java 8 incluye un nuevo tipo llamado CompletableFuture, el cual implementa CompletableStage, que a su vez define el comportamiento de una Promesa, como por ejemplo thenAccept(Consumer<?SuperT>action)
whenComplete(BiConsumer<?superT,?superThrowable>action)
exceptionally(Function<Throwable,?extendsT>fn)
… y muchos otros
@aalmiray
FUTUROS Y PROMESAS JDeferred ofrece un API distinta que permite mejor composición de funciones Promise<D,F,P>then(DoneCallback<D>doneCallback)
Promise<D,F,P>done(DoneCallback<D>callback)
Promise<D,F,P>fail(FailCallback<F>callback)
Promise<D,F,P>always(AlwaysCallback<D,F>callback)
@aalmiray
GET /orgs/${organization}/repos
Lista de datos en formato JSON
https://developer.github.com/v3/
@aalmiray
(1) PRODUCTOR public List<Repository> repositories(final String organization) { try { // operacion costoza en tiempo !! Response<List<Repository>> response = api.repositories(organization).execute(); if (response.isSuccessful()) { return response.body(); } throw new IllegalStateException(response.message()); } catch (IOException e) { throw new IllegalStateException(e); } }
@aalmiray
(2) CONSUMIDOR public class AppController { @Inject private AppModel model; @Inject private GithubAPI api; public void loadRepositories() { model.setState(RUNNING); String organization = model.getOrganization(); try { List<Repository> repositories = repositories(organization); model.getRepositories().addAll(repositories); } catch (Throwable throwable) { handleError(throwable); } finally { model.setState(READY); } } }
@aalmiray
(1) PRODUCTOR public CompletableFuture<List<Repository>> repositories(final String organization) { Supplier<List<Repository>> supplier = () -> { try { // operacion costoza en tiempo !! Response<List<Repository>> response = api.repositories(organization).execute(); if (response.isSuccessful()) { return response.body(); } throw new IllegalStateException(response.message()); } catch (IOException e) { throw new IllegalStateException(e); } }; // operacion ejecutada en un hilo distinto return CompletableFuture.supplyAsync(supplier, executorService); }
@aalmiray
(2) CONSUMIDOR public class AppController { @Inject private AppModel model; @Inject private Github github; public void loadRepositories() { model.setState(RUNNING); github.repositories(model.getOrganization()) .thenAccept(model.getRepositories()::addAll) .exceptionally(throwable -> { handleError(throwable); return null; }) .thenAccept(result -> model.setState(READY)); } }
@aalmiray
(1) PRODUCTOR public Promise<Collection<Repository>, Throwable, Void> repositories(final String organization) { // operacion ejecutada en un hilo distinto return deferredManager.when(() -> { // operacion costoza en tiempo !! Response<List<Repository>> response = api.repositories(organization).execute(); if (response.isSuccessful()) { return response.body(); } throw new IllegalStateException(response.message()); }); }
@aalmiray
(2) CONSUMIDOR public class AppController { @Inject private AppModel model; @Inject private Github github; public void loadRepositories() { model.setState(RUNNING); github.repositories(model.getOrganization()) .done(model.getRepositories()::addAll) .fail(this::handleError) .always((state, resolved, rejected) -> model.setState(READY)); } }
@aalmiray
(1) PRODUCTOR public Observable<Repository> repositories(String organization) { // operacion costoza en tiempo !! Observable<Response<List<Repository>>> observable = api.repositories(organization); return observable.flatMap(response -> { if (response.isSuccessful()) { return Observable.fromIterable(response.body()); } return Observable.error(new HttpResponseException( response.code(), response.message())); }); }
@aalmiray
(2) CONSUMIDOR public class AppController { @Inject private AppModel model; @Inject private Github github; public void load() { Observable<Repository> observable = github.repositories(model.getOrganization()); observable .timeout(10, TimeUnit.SECONDS) .doOnSubscribe(disposable -> model.setState(RUNNING)) .doOnTerminate(() -> model.setState(READY)) .doOnError(this::handleError) // operacion ejecutada en un hilo distinto .subscribeOn(Schedulers.io()) .subscribe(model.getRepositories()::add)); } }
@aalmiray
GET /orgs/${organization}/repos
Lista de datos en formato JSON
Lista de datos en formato JSON
https://developer.github.com/v3/
GET /organizations/1/repos?page=2
@aalmiray
MULTIPLES PAGINAS public Observable<Repository> repositories(String organization) { return paginatedObservable( () -> { return api.repositories(organization); }, (Links links) -> { return api.repositoriesPaginate(links.next()); }); }
@aalmiray
MULTIPLES PAGINAS <T> Observable<T> paginatedObservable(FirstPageSupplier<T> firstPage, NextPageSupplier<T> nextPage) { return processPage(nextPage, firstPage.get()); } <T> Observable<T> processPage(NextPageSupplier<T> supplier, Observable<Response<List<T>>> items) { return items.flatMap(response -> { Links links = Links.of(response.headers().get("Link")); Observable<T> currentPage = Observable.from(response.body()); if (links.hasNext()) { return currentPage.concatWith( processPage(supplier, supplier.get(links))); } return currentPage; }); }
@aalmiray
HTTP://REACTIVEX.IO/ API para Programación Reactiva que provee flujos de datos observables. Existen múltiples implementaciones en más de 20 lenguages.
Java y Javascript implementan el estándar definido por http://www.reactive-streams.org/
@aalmiray
HTTP://REACTIVEX.IO/ Implementaciones en Java: https://github.com/ReactiveX/RxJava
Iniciado por Netflix
https://projectreactor.io
Patrocinado por Pivotal (Spring framework)
@aalmiray
FLUJO DE DATOS Secuencia de valores calculados en el tiempo. Los valores se emiten cuando éstos se encuentran disponibles, sin bloquear a los consumidores.
Los consumidores de un flujo de datos escuchan los cambios en el flujo y reaccionan a dichos cambios.
Modelo push vs pull.
@aalmiray
OBSERVABLE/OBSERVER Los flujos de datos son de tipo Observable mientras que los consumidores son de tipo Observer. El tipo Observable expone una multitud de operaciones que permiten composición, combinaciones, filtros, y/o transformaciones de valores según éstos son añadidos al flujo de datos por el productor.
Ojo: las operaciones en RxJava/Reactor generan un nuevo Observable usando el patrón decorador
@aalmiray
FUNCIONES Las funciones en Programación Funcional usualmente poseen dos características: • No exponen efectos secundarios durante su invocación.
• Dado el mismo conjunto de entradas se obtiene el mismo resultado al invocarlas (idempotencia).
@aalmiray
IMMUTABILIDAD Estructuras de datos immutables y/o contenedores de datos immutables (POJOs) complementan de manera ideal a la programación funcional. Permiten compartir datos entre múltiples consumidores y/o funciones sin temor de que ocurran cambios en los datos originales.
Reducen los puntos de sincronización.
@aalmiray
IMMUTABILIDAD Google’s AutoValue
https://github.com/google/auto/tree/master/value
Lombok’s @Value
https://projectlombok.org/features/Value
Immutables https://immutables.github.io/
@aalmiray
OTRAS OPCIONES http://vertx.io/
Toolkit para crear aplicaciones reactivas. Basado en Netty.
https://grpc.io/
Framework para RPC.
Soporta streaming en ambas direcciones y simultáneo.
@aalmiray
JAVA 9 FLOW Java 9 implementa tambien Reactive Streams con el nuevo tipo java.util.concurrent.Flow Este API permitará combinar streams de distintos proveedores (RxJava , Reactor).
Adiciones al API de CompletableFuture.
@aalmiray
REACTIVE SPRING (WEB) @GetMapping("/accounts/{id}/alerts") public Flux<Alert> getAccountAlerts(@PathVariable Long id) { return this.repository.getAccount(id) .flatMap(account -> this.webClient .perform(get("/alerts/{key}", account.getKey())) .extract(bodyStream(Alert.class))); } https://spring.io/blog/2016/07/28/reactive-programming-with-spring-5-0-m1
@aalmiray
REACTIVE SPRING (DATA+WEB) @RestController class PersonController { private final PersonRepository people; public PersonController(PersonRepository people) { this.people = people; } @GetMapping("/people") Flux<String> namesByLastname(@RequestParam Mono<String> lastname) { Flux<Person> result = repository.findByLastname(lastname); return result.map(it -> it.getFullName()); } } https://spring.io/blog/2016/11/28/going-reactive-with-spring-data
@aalmiray
RECURSOS http://download.java.net/java/jdk9/docs/api/java/util/concurrent/Flow.html http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html http://jdeferred.org/ http://andresalmiray.com/articles/jdeferred-simple-handling-of-promises-and-futures/ http://andresalmiray.com/articles/testing-rxjava2/ https://www.infoq.com/articles/rxjava2-by-example