Acompanhe os novos blogs no site www.byiorio.com.br


Conteúdo do blog:


Teste de performance para o modelo de requests entre serviços - Spring Boot

Objetivo do Teste
Encontrar meios de melhorar a performance para os projetos baseados em Spring Boot no quesito REQUEST vistos no dia a dia.


Desenho do Teste
O Teste consiste em juntar informações de 2 microsserviços e retornar ao cliente.

Caso de uso
Os casos são baseados em situações reais e o endpoint Balance foi dividio em 4 versões.

JAVA
Endpoint:  /{accountNumber}/balance

Versao 1: Usando o restTemaplte
Versão 2: Usando WebCLient bloqueante (uso do método block)
Versão 3: Usando o WebClient não bloqueante  (Uso do Mono)
Versão 4: Usando o Feign Client

Link do projeto de teste
https://github.com/lucasmi/performance_test

Resttemplate
Alguns projetos ainda estão utilizando o restTemplate para fazer as comunicações internas. Neste caso uma das alterações mais significativas para melhorar a performance foi adicionar um PoolManager das conexões.

package br.com.byiorio.performance_test.infra;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestemplateConfig {
    @Bean
    public RestTemplate restTemplate() {

        PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager();
        poolingConnectionManager.setMaxTotal(2000);
        poolingConnectionManager.setDefaultMaxPerRoute(2000);

        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManagerShared(true)
                // .setRedirectStrategy(new LaxRedirectStrategy())
                // .setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE)
                .setConnectionManager(poolingConnectionManager)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        requestFactory.setConnectTimeout(20000);
        requestFactory.setConnectionRequestTimeout(20000);
        requestFactory.setReadTimeout(20000);

        return new RestTemplate(requestFactory);
    }

}


WebClient
Alguns projetos estão usando algumas bibliotecas para chamar outros endpoints, essas bibliotecas estão fazendo o block() na requisição, e mesmo usando webclient, o block torna tudo síncrono tendo o mesmo efeito que o resttemplate.

O ideal é Retornar o Mono no método, mudando o código abaixo para o modo assíncrono mostrado mais à frente. Lembrando que abaixo está uma representação do código, na vida real os blocks são executados em outras funções internas.

    public BalanceResponse usandoWebClientComBlock(Integer accountNumber) {
        BalanceResponse balanceResponse = new BalanceResponse();

        // Primeira Chamada
        Mono<CardResponse> card = httpClientLocalhost
                .get()
                .uri(HTTP_LOCALHOST_9090.concat(accountNumber.toString()).concat("/card"))
                .retrieve()
                .bodyToMono(CardResponse.class);

        CardResponse cardResponse = card.block();

        if (cardResponse != null) {
            balanceResponse.setCardNumber(cardResponse.getCardNumber());
        }

        // Segunda Chamada
        Mono<StatusResponse> status = httpClientLocalhost
                .get()
                .uri(HTTP_LOCALHOST_9091.concat(accountNumber.toString()).concat("/status"))
                .retrieve()
                .bodyToMono(StatusResponse.class);

        StatusResponse statusResponse = status.block();

        if (statusResponse != null) {
            balanceResponse.setStatus(statusResponse.getCode());
        }

        // Adiciona o balance
        balanceResponse.setBalance(getBalance(accountNumber));

        return balanceResponse;
    }

Para acontecer o paralelismo é bom sempre retornar o MONO e no momento do processamento usar métodos de apoio como o Mono.zip, o ZIP inicia o processamento assim que todas as requisições paralelas foram entregues.

public Mono<BalanceResponse> usandoWebClientSemBlock(Integer accountNumber) {

        // Primeira Chamada
        Mono<CardResponse> card = httpClientLocalhost
                .get()
                .uri(HTTP_LOCALHOST_9090.concat(accountNumber.toString()).concat("/card"))
                .retrieve()
                .bodyToMono(CardResponse.class);

        // Segunda Chamada
        Mono<StatusResponse> status = httpClientLocalhost
                .get()
                .uri(HTTP_LOCALHOST_9091.concat(accountNumber.toString()).concat("/status"))
                .retrieve()
                .bodyToMono(StatusResponse.class);

        // Execução em paralelo
        return Mono.zip(card, status)
                .map(respostas -> {

                    return BalanceResponse.builder()
                            .status(respostas.getT2().getCode())
                            .cardNumber(respostas.getT1().getCardNumber())
                            .balance(this.getBalance(accountNumber))
                            .build();
                });
    }


Feign Client
Baseado no site https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/  foram construídas as interfaces e o método abaixo, as execuções ficaram síncronas, resultando no mesmo que o restTemplate e webclient.block().

Oficialmente o Feign não tem suporte ao desenvolvimento Reativo, conforme o link https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#reactive-support, mas é possível utilizar o https://github.com/Playtika/feign-reactive para tentar o paralelismo.

    public BalanceResponse usandoFeign(Integer accountNumber) {
        BalanceResponse balanceResponse = new BalanceResponse();

        // Primeira Chamada
        CardResponse cardResponse = feignCardClient.getCard(accountNumber);

        if (cardResponse != null) {
            balanceResponse.setCardNumber(cardResponse.getCardNumber());
        }

        // Segunda Chamada
        StatusResponse statusResponse = feignStatusClient.getStatus(accountNumber);

        if (statusResponse != null) {
            balanceResponse.setStatus(statusResponse.getCode());
        }

        // Adiciona o balance
        balanceResponse.setBalance(getBalance(accountNumber));

        return balanceResponse;
    }

Conclusão
O melhor resultado para este cenário está no uso do WebClient Reativo, onde processou o dobro de requisições por causa de seu paralelismo nas chamadas.

Veja o próximo artigo de como fazer o Feign Client em paralelo, https://www.byiorio.com.br/blog/2

performance teste, spring boot, rest, api, melhorar, improove




Redirecionar para https://www.byiorio.com.br/blog/1