Cliente HTTP Jetty ReactiveStreams
1. Visão geral
Neste tutorial, vamos aprender como usar oReactive HTTP client from Jetty. Estaremos demonstrando seu uso com diferentes bibliotecas reativas criando pequenos casos de teste.
2. O que éHttpClient reativo?
OHttpClient do Jetty nos permite realizar o bloqueio de solicitações HTTP. Quando estamos lidando com uma API reativa, no entanto, não podemos usar o cliente HTTP padrão. Para preencher essa lacuna, o Jetty criou um wrapper em torno das APIsHttpClient para que também suporte a APIReactiveStreams.
OHttpClient reativo é usado para consumir ou produzir um fluxo de dados em chamadas HTTP.
O exemplo que vamos demonstrar aqui terá um cliente HTTP Reactive, que se comunicará com um servidor Jetty usando diferentes bibliotecas Reactive. Também falaremos sobre os eventos de solicitação e resposta fornecidos por ReactiveHttpClient.
Recomendamos a leitura de nossos artigos emProject Reactor,RxJava eSpring WebFlux para obter uma melhor compreensão dos conceitos de programação reativa e suas terminologias.
3. Dependências do Maven
Vamos começar o exemplo adicionando dependências paraReactive Streams,Project Reactor,RxJava,Spring WebFlux eJetty’s Reactive HTTPClient ao nossopom.xml. . Juntamente com eles, nós ' estarei adicionando a dependência deJetty Server também para a criação do servidor:
org.eclipse.jetty
jetty-reactive-httpclient
1.0.3
org.eclipse.jetty
jetty-server
9.4.19.v20190610
org.reactivestreams
reactive-streams
1.0.3
io.projectreactor
reactor-core
3.2.12.RELEASE
io.reactivex.rxjava2
rxjava
2.2.11
org.springframework
spring-webflux
5.1.9.RELEASE
4. Criando o Servidor e o Cliente
Agora vamos criar um servidor e adicionar um manipulador de solicitação que simplesmente grava o corpo da solicitação na resposta:
public class RequestHandler extends AbstractHandler {
@Override
public void handle(String target, Request jettyRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
jettyRequest.setHandled(true);
response.setContentType(request.getContentType());
IO.copy(request.getInputStream(), response.getOutputStream());
}
}
...
Server server = new Server(8080);
server.setHandler(new RequestHandler());
server.start();
E então podemos escrever oHttpClient:
HttpClient httpClient = new HttpClient();
httpClient.start();
Agora que criamos o cliente e o servidor, vamos ver como podemos transformar esse cliente HTTP bloqueador em um não bloqueador e criar a solicitação:
Request request = httpClient.newRequest("http://localhost:8080/");
ReactiveRequest reactiveRequest = ReactiveRequest.newBuilder(request).build();
Publisher publisher = reactiveRequest.response();
Então, aqui, o wrapperReactiveRequest fornecido pelo Jetty tornou nosso cliente HTTP bloqueador reativo. Vamos prosseguir e ver seu uso com diferentes bibliotecas reativas.
5. ReactiveStreams Uso
HttpClient do Jetty suporta nativamenteReactive Streams, então vamos começar por aí.
Agora,Reactive Streams is just a set of interfaces, então, para nossos testes, vamos implementar um assinante de bloqueio simples:
public class BlockingSubscriber implements Subscriber {
BlockingQueue sink = new LinkedBlockingQueue<>(1);
@Override
public void onSubscribe(Subscription subscription) {
subscription.request(1);
}
@Override
public void onNext(ReactiveResponse response) {
sink.offer(response);
}
@Override
public void onError(Throwable failure) { }
@Override
public void onComplete() { }
public ReactiveResponse block() throws InterruptedException {
return sink.poll(5, TimeUnit.SECONDS);
}
}
Observe que precisamoscall Subscription#request de acordo com o JavaDoc, que afirma que“No events will be sent by a Publisher until demand is signaled via this method.”
Além disso, observe que adicionamos um mecanismo de segurança para que nosso teste possa resgatar se não tiver visto o valor em 5 segundos.
E agora, podemos testar rapidamente nossa solicitação HTTP:
BlockingSubscriber subscriber = new BlockingSubscriber();
publisher.subscribe(subscriber);
ReactiveResponse response = subscriber.block();
Assert.assertNotNull(response);
Assert.assertEquals(response.getStatus(), HttpStatus.OK_200);
6. Uso do reator do projeto
Vamos agora ver como podemos usar o ReactiveHttpClient com o Project Reactor. A criação do editor é praticamente a mesma da seção anterior.
Após a criação do editor, vamos usar a classeMono do Project Reactor para obter uma resposta reativa:
ReactiveResponse response = Mono.from(publisher).block();
E então, podemos testar a resposta resultante:
Assert.assertNotNull(response);
Assert.assertEquals(response.getStatus(), HttpStatus.OK_200);
6.1. Uso do Spring WebFlux
A conversão do cliente HTTP de bloqueio em um reativo é fácil quando usada com o Spring WebFlux. The Spring WebFlux ships with a reactive client, WebClient, that can be used with various HTTP Client libraries. Podemos usar isso como uma alternativa ao uso direto do código do Project Reactor.
Então, primeiro, vamos embrulhar o cliente HTTP do Jetty usandoJettyClientHttpConnector para ligá-lo aoWebClient:
ClientHttpConnector clientConnector = new JettyClientHttpConnector(httpClient);
E, em seguida, passe este conector paraWebClient para realizar as solicitações HTTP sem bloqueio:
WebClient client = WebClient.builder().clientConnector(clientConnector).build();
A seguir, vamos fazer a chamada HTTP real com o cliente HTTP reativo que acabamos de criar e testar o resultado:
String responseContent = client.post()
.uri("http://localhost:8080/").contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromPublisher(Mono.just("Hello World!"), String.class))
.retrieve()
.bodyToMono(String.class)
.block();
Assert.assertNotNull(responseContent);
Assert.assertEquals("Hello World!", responseContent);
7. RxJava2 Uso
Vamos agora seguir em frente e ver como o cliente HTTP reativo é usado com RxJava2.
Enquanto estamos aqui, vamos modificar nosso exemplo um pouco para incluir um corpo na solicitação:
ReactiveRequest reactiveRequest = ReactiveRequest.newBuilder(request)
.content(ReactiveRequest.Content
.fromString("Hello World!", "text/plain", StandardCharsets.UTF_8))
.build();
Publisher publisher = reactiveRequest
.response(ReactiveResponse.Content.asString());
O códigoReactiveResponse.Content.asString() converte o corpo da resposta em uma string. Também é possível descartar a resposta usando o métodoReactiveResponse.Content.discard() se estivermos interessados apenas no status da solicitação.
Agora, podemos ver que obter uma resposta usando o RxJava2 é realmente muito semelhante ao Project Reactor. Basicamente, usamos apenasSingle em vez deMono:
String responseContent = Single.fromPublisher(publisher)
.blockingGet();
Assert.assertEquals("Hello World!", responseContent);
8. Eventos de solicitação e resposta
O cliente HTTP reativo emite vários eventos durante a execução. Eles são categorizados como eventos de solicitação e resposta. Esses eventos são úteis para dar uma olhada no ciclo de vida de um cliente HTTP reativo.
Desta vez, vamos fazer nossa solicitação reativa de maneira um pouco diferente usando o cliente HTTP em vez da solicitação:
ReactiveRequest request = ReactiveRequest.newBuilder(httpClient, "http://localhost:8080/")
.content(ReactiveRequest.Content.fromString("Hello World!", "text/plain", UTF_8))
.build();
E agora vamos obterPublisher de eventos de solicitação HTTP:
Publisher requestEvents = request.requestEvents();
Agora, vamos usar o RxJava mais uma vez. Desta vez, criaremos uma lista que contém os tipos de evento e a preencheremos inscrevendo-se nos eventos de solicitação conforme eles acontecem:
List requestEventTypes = new ArrayList<>();
Flowable.fromPublisher(requestEvents)
.map(ReactiveRequest.Event::getType).subscribe(requestEventTypes::add);
Single response = Single.fromPublisher(request.response());
Então, como estamos em um teste, podemos bloquear nossa resposta e verificar:
int actualStatus = response.blockingGet().getStatus();
Assert.assertEquals(6, requestEventTypes.size());
Assert.assertEquals(HttpStatus.OK_200, actualStatus);
Da mesma forma, também podemos assinar os eventos de resposta. Como eles são semelhantes à inscrição do evento de solicitação, adicionamos apenas o último aqui. A implementação completa com os eventos de solicitação e resposta pode ser encontrada no repositório GitHub, vinculado no final deste artigo.
9. Conclusão
Neste tutorial, aprendemos sobre oReactiveStreams HttpClient fornecido pelo Jetty, seu uso com as várias bibliotecas reativas e os eventos de ciclo de vida associados a uma solicitação reativa.
Todos os trechos de código, mencionados no artigo, podem ser encontrados em nossoGitHub repository.