Vamos falar sobre RSocket

Olá,

É de conhecimento comum que atualmente a arquitetura de microserviços é o padrão atual de construções de aplicações escaláveis e que rodam principalmente em cloud, dai o nome do que muito se fala hoje em dia: Cloud Native. Pois bem, com a chegada do manifesto reativo e diverssas questões relacioadas a comunicação assincrona, além de uma facilidade maior de resiliencia, escalabilidade e etc. Acabaram surgindo implementações como Reactive Streams, ReactiveX e muitos outros que no fundo buscam dar uma cara para aquilo que chamamos de programação reativa.

Bem, um outro fato importante na implementação de soluçoes que utilizam microserviços é que conforme o número de elementos da solução cresce, cresce também o número de chamadas na rede referentes as comunicações entre esses microserviços, acabamos então em um cenário em que trocar de HTTP/JSON para um protocolo que seja mais “binário”, temos um enorme salto de performance e economia de recursos computacionais.

Diagrama do “Reactive Manifesto”

“Without Further Ado”, onde entra o RSocket nisso tudo? Este protocolo é mais uma das ideias geniais criadas pela equipe de desenvolvimento da Netflix e sua intenção é ser utilizado em cima de protolos binários que normalmente são responsáveis pelo tranporte de bytes na forma de fluxos como, por exemplo : “TCP”, “WebSocket”, “Aeron”.

Modelos de Iteração

Aprofundando um pouco sobre o funcionamento desse protocolo, notamos de cara um ganho interessante se comparado por exemplo ao tradicional request/response com HTTP/JSON. Além desse tipo, com o RSocket encontramos suporte a outros modelos como, por exemplo:

  • request/response (stream of 1)
  • request/stream (finite stream of many)
  • fire-and-forget (no response)
  • channel (bi-directional streams)
Modelos de iteração

Trocando em miudos isso significa que você pode trabalhar com mensagens binárias sem conversão e que são mais naturalmente expressivas se analisadas pela maneira como servidor deve tratar cada requisição, quando eu falo isso eu me refiro por exemplo as inumeras gambiarras que os programadores tem que fazer com HTTP para implementar os modelos de iteração que mencionei anteriormente, já no caso do RSsocket é parte fundamental e natural do modo de trabalhar. Você também obtém controles modernos, como: multiplexing, back-pressure, resumption, and routing.

Para a coisa toda não ficar só na teoria, implementei um exemplo para mostrar como isso tudo funciona na prática, claro que com spring tudo fica mais fácil, pois como programador, ficamos basicamente com a parte que mais importa, a implementação da regra de negócio.

Criando um servidor RSocket com Spring Boot

Antes de tudo, vamos começar utilizando claro o initializer, selecionando o starter rsocket, conforme imagem abaixo:

Apenas algumas recomendações:

  • Tente usar o spring 2.3.3, por razões que já apresentei por aqui, é a versão mais recente, suporte a gracefull shutdown, buildpacks e etc..
  • Tente usar Java 14, por que antes disso já existem muitas vulnerabilidades e já não mais sentido para projetos novos.
  • Sempre adicione: Lombok, DevTools e Configuration Processor aos seus projetos, isso facilita muito a vida.

Para testar nosso pequeno exemplo, recomendo a utilização de um programinha bem interessante, feito especialmente para trabalhar com RSocket, principalmente quando queremos “debugar” o conteudo do stream , esse utilitário de linha de comando se chama RSC, e foi criado pelo genial Toshiaki Maki, funcionário da VMWare.

Após baixar ele para a maquina, sua execução é bastante simples, por ser um jar basta então executar

$ jar java -jar rsc-0.5.0.jar <passando aqui os argumentos, claro>

Com relação ao projeto Java, organizei ele da seguinte forma pra ficar intuitivo, no pacote configuration estou adicionando um bean de configuração para tratar resumption de conexões, esse é um tópico interessante, saiba apenas que estou tornando as configuraões padrões.

No pacote controller fica a classe que realmente vai receber as requisições e trabalhar a mensagem criada no pacote domain.

Estrutura do Projeto

Agora sim, vamos entender os quatro tipos de comunicação, o que vai mudar em cada um é a maneira como vou trabalhar com Mono, Flux e etc.

O uso da anotação @MessageMapping é apenas para que eu possa usar uma rota e controlar o metodo que será chamado.

Request/Response

No exemplo abaixo ao receber uma chamada apenas crio um stream de 1 elemento e retorno, esse é o modelo que mais se parece com o tradicionalmente conhecido HTTP.

@MessageMapping("request-response")
Mono<Message> requestResponse(final Message request) {
    log.info("Received request-response request: {}", request);
    // create a single Message and return it
    return Mono.just(new Message(SERVER, RESPONSE));
}

Com a aplicação Sprint Boot rodando, abra um terminal e execute o comando:

$ java -jar rsc-0.5.0.jar --debug --request --data "{\"origin\":\"Client\",\"interaction\":\"Request\"}" --route request-response tcp://localhost:7000

e o que vamos obter é simplesmente o detalhe da comunicação:

Fire and forget

Nesse modelo ao receber uma requisição eu posso dar inicio a um processo interno de forma assincrona e meu cliente pode continuar fazendo outra coisa, aqui estou retornando um “Mono” vazio apenas para fins didáticos.

@MessageMapping("fire-and-forget")
public Mono<Void> fireAndForget(final Message request) {
    log.info("Received fire-and-forget request: {}", request);    
    return Mono.empty();
}

Com a aplicação spring boot rodando, do mesmo modo que fizemos anteriormente basta abrir o terminal e executar:

$ java -jar rsc-0.5.0.jar --debug --fnf --data "{\"origin\":\"Client\",\"interaction\":\"Request\"}" --route fire-and-forget tcp://localhost:7000

Stream

Esse modelo é ideal para quando existe a necessidade de transferir multiplas informações atraves de um stream continuo, não entendeu? Imagina um filme de 2 horas de duração, fazendo assim você envia periodicamente uma porcentagem de bytes e seu cliente ainda pode controlar o que chamamos de “back-pressure”.

Para um exemplo didático estou criando um Flux que retorna continuamente um stream de bytes a cada segundo.

@MessageMapping("stream")
Flux<Message> stream(final Message request) {
    log.info("Received stream request: {}", request);
    return Flux.interval(Duration.ofSeconds(1))            
            .map(index -> new Message(SERVER, STREAM, index));
}

Esse é comando para realizar o teste com stream:

$ java -jar rsc-0.5.0.jar --debug --stream --data "{\"origin\":\"Client\",\"interaction\":\"Request\"}" --route stream tcp://localhost:7000

Um fluxo continuo de informação acaba sendo recebido no console.

Channel

Po último, temos o modelo de canal, que resumidamente é full-duplex, permitindo enviar e receber informação dentro da mesma chamada, aqui é onde vemos algo bem parecido com o tradicional WebSocket.

@MessageMapping("channel")
Flux<Message> channel(final Flux<Duration> settings) {
    log.info("Received channel request...");
    return settings
            .doOnNext(setting -> log.info("Channel frequency setting is {} second(s).", setting.getSeconds()))
            .doOnCancel(() -> log.warn("The client cancelled the channel."))
            .switchMap(setting -> Flux.interval(setting)
                    .map(index -> new Message(SERVER, CHANNEL, index)));
}

O comando logo abaixo inicia um canal com duração de 5 segundos:

$ java -jar rsc-0.5.0.jar --debug --channel --data "5 seconds" --route channel tcp://localhost:7000

Isso é tudo pessoal, espero que tenham gostado, o código fonte do exemplo que eu criei pode ser baixado aqui:

https://github.com/natanaelfonseca/pstk-rsocket-playground

Happy Coding.
Natanael Fonseca

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s