Ratpack com RxJava
1. Introdução
RxJava é uma das bibliotecas de programação reativa mais populares que existem.
ERatpack é uma coleção de bibliotecas Java para a criação de aplicativos da web simples e poderosos construídos no Netty.
Neste tutorial, discutiremos a incorporação de RxJava em um aplicativo Ratpack para criar um aplicativo da web reativo agradável.
2. Dependências do Maven
Agora, primeiro precisamos das dependênciasratpack-core andratpack-rx :
io.ratpack
ratpack-core
1.6.0
io.ratpack
ratpack-rx
1.6.0
Observe, a propósito, queratpack-rx importa a dependência derxjava para nós.
3. Configuração inicial
O RxJava suporta a integração de bibliotecas de terceiros, usando seu sistema de plugins. So, we can incorporate different execution strategies into RxJava’s execution model.
Ratpack se conecta a este modelo de execução por meio deRxRatpack, que inicializamos na inicialização:
RxRatpack.initialise();
Agora, é importante observar quethe method needs to be called only once per JVM run.
O resultado é quewe’ll be able to map RxJava’s Observables into RxRatpack’s Promise types e vice-versa.
4. Observables aPromises
Podemos converter umObservable em RxJava em um RatpackPromise.
However, there is a bit of a mismatch. Veja, aPromise emita um único valor, mas uma varreduraObservable emite um fluxo deles.
RxRatpack lida com isso oferecendo dois métodos diferentes:promiseSingle()epromise().
Então, vamos supor que temos um serviço chamadoMovieService que emite uma única promessa emgetMovie(). UsaríamospromiseSingle(), pois sabemos que ele emitirá apenas uma vez:
Handler movieHandler = (ctx) -> {
MovieService movieSvc = ctx.get(MovieService.class);
Observable movieObs = movieSvc.getMovie();
RxRatpack.promiseSingle(movieObs)
.then(movie -> ctx.render(Jackson.json(movie)));
};
Por outro lado, segetMovies() puder retornar um fluxo de resultados de filme, usaríamospromise():
Handler moviesHandler = (ctx) -> {
MovieService movieSvc = ctx.get(MovieService.class);
Observable movieObs = movieSvc.getMovies();
RxRatpack.promise(movieObs)
.then(movie -> ctx.render(Jackson.json(movie)));
};
Em seguida, podemos adicionar esses manipuladores ao nosso servidor Ratpack normalmente:
RatpackServer.start(def -> def.registryOf(rSpec -> rSpec.add(MovieService.class, new MovieServiceImpl()))
.handlers(chain -> chain
.get("movie", movieHandler)
.get("movies", moviesHandler)));
5. Promises aObservables
Por outro lado, podemos mapear um tipoPromise no Ratpack de volta para um RxJavaObservable.
RxRatpack novamente tem dois métodos:observe()eobserveEach().
Neste caso, vamos imaginar que temos um serviço de filme que retornaPromises em vez deObservables.
Com nossogetMovie(), we'd usariaobserve():
Handler moviePromiseHandler = ctx -> {
MoviePromiseService promiseSvc = ctx.get(MoviePromiseService.class);
Promise moviePromise = promiseSvc.getMovie();
RxRatpack.observe(moviePromise)
.subscribe(movie -> ctx.render(Jackson.json(movie)));
};
E quando obtivermos uma lista, como comgetMovies(), usaríamosobserveEach():
Handler moviesPromiseHandler = ctx -> {
MoviePromiseService promiseSvc = ctx.get(MoviePromiseService.class);
Promise> moviePromises = promiseSvc.getMovies();
RxRatpack.observeEach(moviePromises)
.toList()
.subscribe(movie -> ctx.render(Jackson.json(movie)));
};
Então, novamente, podemos adicionar os manipuladores conforme o esperado:
RatpackServer.start(def -> def.registryOf(regSpec -> regSpec
.add(MoviePromiseService.class, new MoviePromiseServiceImpl()))
.handlers(chain -> chain
.get("movie", moviePromiseHandler)
.get("movies", moviesPromiseHandler)));
6. Processamento paralelo
RxRatpack suporta paralelismo com a ajuda dos métodosfork()eforkEach().
E segue um padrão que já vimos em cada um.
fork() leva um únicoObservableeparallelizes its execution onto a different compute thread. Em seguida, vincula automaticamente os dados à execução original.
Por outro lado,forkEach() faz o mesmo para cada elemento emitido por um fluxo de valores deObservable.
Vamos imaginar por um momento que queremos capitalizar nossos títulos de filmes e que essa é uma operação cara.
Simplificando, podemos usarforkEach() para descarregar a execução de cada um em um pool de threads:
Observable movieObs = movieSvc.getMovies();
Observable upperCasedNames = movieObs.compose(RxRatpack::forkEach)
.map(movie -> movie.getName().toUpperCase())
.serialize();
7. Tratamento implícito de erros
Por fim, o tratamento implícito de erros é um dos principais recursos da integração do RxJava.
Por padrão, as seqüências observáveis do RxJava encaminham qualquer exceção para o manipulador de exceção do contexto de execução. Por esse motivo, os manipuladores de erros não precisam ser definidos em sequências observáveis.
Então,we can configure Ratpack to handle these errors raised by RxJava.
Digamos, por exemplo, que desejamos que cada erro seja impresso na resposta HTTP.
Observe que a exceção que lançamos por meio deObservable é capturada e tratada por nossoServerErrorHandler:
RatpackServer.start(def -> def.registryOf(regSpec -> regSpec
.add(ServerErrorHandler.class, (ctx, throwable) -> {
ctx.render("Error caught by handler : " + throwable.getMessage());
}))
.handlers(chain -> chain
.get("error", ctx -> {
Observable. error(new Exception("Error from observable")).subscribe(s -> {});
})));
Note that any subscriber-level error handling takes precedence, though. Se nossoObservable quisesse fazer seu próprio tratamento de erros, poderia, mas, como não faz, a exceção se infiltrou no Ratpack.
8. Conclusão
Neste artigo, falamos sobre como configurar o RxJava com o Ratpack.
Exploramos as conversões deObservables em RxJava paraPromise tipos em Ratpack e vice-versa. Também analisamos os paralelismo e os recursos implícitos de manipulação de erros suportados pela integração.
Todos os exemplos de código usados para o artigo podem ser encontradosover at Github.