RSocket usando o Spring Boot
1. Visão geral
O RSocket é um protocolo de aplicação que fornece a semântica de Fluxos Reativos - ele funciona, por exemplo, como uma alternativa ao HTTP.
Neste tutorial, veremosRSocket usandoSpring Boot e, especificamente, como isso ajuda a abstrair a API RSocket de nível inferior.
2. Dependências
Vamos começar adicionando a dependênciaspring-boot-starter-rsocket:
org.springframework.boot
spring-boot-starter-rsocket
Isso irá extrair transitivamente as dependências relacionadas ao RSocket, comorsocket-coreersocket-transport-netty.
3. Aplicação de amostra
Agora continuaremos com nosso aplicativo de amostra. Para destacar os modelos de interação que o RSocket oferece, vamos criar um aplicativo de trader. Nosso aplicativo de trader consistirá em um cliente e um servidor.
3.1. Configuração do servidor
Primeiro, vamos configurar o servidor, que será um aplicativo Spring Boot inicializando um servidor RSocket.
Since we have the spring-boot-starter-rsocket dependency, Spring Boot autoconfigures an RSocket server for us. Como de costume com Spring Boot, podemos alterar os valores de configuração padrão para o servidor RSocket em um modo controlado por propriedade.
Por exemplo, vamos mudar a porta do nosso servidor RSocket adicionando a seguinte linha ao nosso arquivoapplication.properties:
spring.rsocket.server.port=7000
Também podemos alterarother properties para modificar ainda mais nosso servidor de acordo com nossas necessidades.
3.2. Configuração do cliente
A seguir, vamos configurar o cliente, que também será um aplicativo Spring Boot.
Embora o Spring Boot configure automaticamente a maioria dos componentes relacionados ao RSocket, também devemos definir alguns beans para concluir a instalação:
@Configuration
public class ClientConfiguration {
@Bean
public RSocket rSocket() {
return RSocketFactory
.connect()
.mimeType(MimeTypeUtils.APPLICATION_JSON_VALUE, MimeTypeUtils.APPLICATION_JSON_VALUE)
.frameDecoder(PayloadDecoder.ZERO_COPY)
.transport(TcpClientTransport.create(7000))
.start()
.block();
}
@Bean
RSocketRequester rSocketRequester(RSocketStrategies rSocketStrategies) {
return RSocketRequester.wrap(rSocket(), MimeTypeUtils.APPLICATION_JSON, rSocketStrategies);
}
}
Aqui, estamos criando o clienteRSocket e configurando-o para usar o transporte TCP na porta 7000. Observe que esta é a porta do servidor que configuramos anteriormente.
Em seguida, estamos definindo um beanRSocketRequester que é um envoltório em torno deRSocket. Esse bean nos ajudará a interagir com o servidor RSocket.
Depois de definir essas configurações de bean, temos uma estrutura básica.
A seguir,we’ll explore different interaction modelse veja como o Spring Boot nos ajuda nisso.
4. Request/Response with RSocket and Spring Boot
Vamos começar com Solicitação / Resposta. This is probably the most common and familiar interaction model since HTTP also employs this type of communication.
Nesse modelo de interação, o cliente inicia a comunicação e envia uma solicitação. Posteriormente, o servidor executa a operação e retorna uma resposta ao cliente - assim, a comunicação é concluída.
Em nosso aplicativo de negociação, um cliente solicitará os dados atuais de mercado de uma determinada ação. Em troca, o servidor passará os dados solicitados.
4.1. Servidor
No lado do servidor, devemos primeiro criar um controlador para armazenar nossos métodos manipuladores. But instead of @RequestMapping or @GetMapping annotations like in Spring MVC, we will use the @MessageMapping annotation:
@Controller
public class MarketDataRSocketController {
private final MarketDataRepository marketDataRepository;
public MarketDataRSocketController(MarketDataRepository marketDataRepository) {
this.marketDataRepository = marketDataRepository;
}
@MessageMapping("currentMarketData")
public Mono currentMarketData(MarketDataRequest marketDataRequest) {
return marketDataRepository.getOne(marketDataRequest.getStock());
}
}
Então, vamos investigar nosso controlador.
Estamos usando a anotação @Controller para definir um manipulador que deve processar as solicitações RSocket de entrada. Além disso, a anotação@MessageMapping nos permite definir em qual rota estamos interessados e como reagir a uma solicitação.
Nesse caso, o servidor escuta a rotacurrentMarketData, quereturns a single result to the client as aMono<MarketData>.
4.2. Cliente
Em seguida, nosso cliente RSocket deve solicitar o preço atual de uma ação e obter uma única resposta.
Para iniciar a solicitação, devemos usar a classeRSocketRequester:
@RestController
public class MarketDataRestController {
private final RSocketRequester rSocketRequester;
public MarketDataRestController(RSocketRequester rSocketRequester) {
this.rSocketRequester = rSocketRequester;
}
@GetMapping(value = "/current/{stock}")
public Publisher current(@PathVariable("stock") String stock) {
return rSocketRequester
.route("currentMarketData")
.data(new MarketDataRequest(stock))
.retrieveMono(MarketData.class);
}
}
Observe que, no nosso caso, o cliente RSocket também é um controlador REST do qual chamamos nosso servidor RSocket. Então, estamos usando@RestController e@GetMapping para definir nosso endpoint de solicitação / resposta.
No método do endpoint, estamos usandoRSocketRequestere especificando a rota. De fato, esta é a rota que o servidor RSocket espera. Então, estamos transmitindo os dados da solicitação. E, por último,when we call the retrieveMono() method, Spring Boot initiates a request/response interaction.
5. Fogo e esqueça com RSocket e Spring Boot
A seguir, veremos o modelo de interação dispare e esqueça. Como o nome indica, o cliente envia uma solicitação ao servidor, mas não espera uma resposta de volta.
Em nosso aplicativo de trader, alguns clientes servirão como fonte de dados e enviarão dados de mercado para o servidor.
5.1. Servidor
Vamos criar outro endpoint em nosso aplicativo de servidor:
@MessageMapping("collectMarketData")
public Mono collectMarketData(MarketData marketData) {
marketDataRepository.add(marketData);
return Mono.empty();
}
Novamente, estamos definindo um novo@MessageMapping com o valor da rota decollectMarketData. Além disso, o Spring Boot converte automaticamente a carga de entrada em uma instânciaMarketData.
A grande diferença aqui, porém, é quewe return a Mono<Void> since the client doesn’t need a response from us.
5.2. Cliente
Vamos ver como podemos iniciar nosso pedido de fogo e esqueça.
Vamos criar outro endpoint REST:
@GetMapping(value = "/collect")
public Publisher collect() {
return rSocketRequester
.route("collectMarketData")
.data(getMarketData())
.send();
}
Aqui estamos especificando nossa rota e nossa carga útil será uma instânciaMarketData. Since we’re using the send() method to initiate the request instead of retrieveMono(), the interaction model becomes fire-and-forget.
6. Solicitar fluxo com RSocket e Spring Boot
O fluxo de solicitações é um modelo de interação mais envolvido, no qual o cliente envia uma solicitação, mas obtém várias respostas ao longo do tempo do servidor.
Para simular esse modelo de interação, o cliente solicitará todos os dados de mercado de uma determinada ação.
6.1. Servidor
Vamos começar com nosso servidor. Adicionaremos outro método de mapeamento de mensagem:
@MessageMapping("feedMarketData")
public Flux feedMarketData(MarketDataRequest marketDataRequest) {
return marketDataRepository.getAll(marketDataRequest.getStock());
}
Como podemos ver, esse método manipulador é muito semelhante aos outros. A parte diferente é quewe returning a Flux<MarketData> instead of a Mono<MarketData>. No final, nosso servidor RSocket enviará várias respostas ao cliente.
6.2. Cliente
No lado do cliente, devemos criar um terminal para iniciar nossa solicitação / comunicação de fluxo:
@GetMapping(value = "/feed/{stock}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Publisher feed(@PathVariable("stock") String stock) {
return rSocketRequester
.route("feedMarketData")
.data(new MarketDataRequest(stock))
.retrieveFlux(MarketData.class);
}
Vamos investigar nossa solicitação RSocket.
Primeiro, estamos definindo a rota e a carga útil da solicitação. Then, we’re defining our response expectation with the retrieveFlux() method call. Esta é a parte que determina o modelo de interação.
Observe também que, como nosso cliente também é um servidor REST, ele define o tipo de mídia de resposta comoMediaType.TEXT_EVENT_STREAM_VALUE.
7. Manipulação de exceção
Agora vamos ver como podemos lidar com exceções em nosso aplicativo de servidor de forma declarativa.
Ao fazer solicitação / resposta, podemos simplesmente usar a anotação@MessageExceptionHandler:
@MessageExceptionHandler
public Mono handleException(Exception e) {
return Mono.just(MarketData.fromException(e));
}
Aqui, anotamos nosso método de tratamento de exceção com@MessageExceptionHandler. Como resultado, ele tratará todos os tipos de exceções, já que a classeException é a superclasse de todas as outras.
Podemos ser mais específicos e criar diferentes métodos de tratamento de exceções para diferentes tipos de exceção.
Isso é claro para o modelo de solicitação / resposta e, portanto, estamos retornando umMono<MarketData>.We want our return type here to match the return type of our interaction model.
8. Sumário
Neste tutorial, cobrimos o suporte RSocket do Spring Boot e detalhamos diferentes modelos de interação que o RSocket fornece.
Como sempre, você pode verificar todas as amostras de códigoover on GitHub.