Download - Introduccion a Programacion Reactiva

Transcript

INTRODUCCION A PROGRAMACION REACTIVA

ANDRES ALMIRAY @AALMIRAY ANDRESALMIRAY.COM

@aalmiray

@aalmiray

JCP Executive Committee Associate Seat

Committer

Committer

JSR377 Specification Lead

@aalmiray

CONCEPTOS

@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

EJEMPLOS

@aalmiray

GET /orgs/${organization}/repos

Lista de datos en formato JSON

https://developer.github.com/v3/

@aalmiray

OBTEN EL CODIGO EN:

https://github.com/aalmiray/javatrove/

@aalmiray

IMPERATIVO :’(

@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

PROMESAS ;-) (JAVA8)

@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

PROMESAS ;-) (JDEFERRED)

@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

REACTIVO :D (RXJAVA)

@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

FLUJOS DE DATOS (STREAMS)

@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

OPERACIONES (UNA MUESTRA A SEGUIR)

@aalmiray

HTTP://RXMARBLES.COM

@aalmiray

HTTP://RXMARBLES.COM

@aalmiray

HTTP://RXMARBLES.COM

@aalmiray

HTTP://RXMARBLES.COM

@aalmiray

HTTP://REACTIVEX.IO/RXJAVA/2.X/JAVADOC/INDEX.HTML

@aalmiray

PROGRAMACION FUNCIONAL

@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

EL FUTURO

@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

@aalmiray

LA PROGRAMACION REACTIVA IMPLICA

UN CAMBIO DE PARADIGMA!

@aalmiray

HTTP://ANDRESALMIRAY.COM/NEWSLETTER

HTTP://ANDRESALMIRAY.COM/EDITORIAL

@aalmiray

@aalmiray

http://www.jespanol.org

GRACIAS!

ANDRES ALMIRAY @AALMIRAY ANDRESALMIRAY.COM