RSocket usando o Spring Boot

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.