Microservices - Roteamento dinâmico

Por Gaspar Barancelli Junior em 04 de abril de 2024

Esse é o terceiro post sobre microservices.

  1. Configurações

  2. Registro de serviços e descobertas

  3. Roteamento dinâmico

  4. Comunicação

  5. Falhas

  6. Rastreamento

  7. Logs

  8. Documentações

  9. Monitoramento

  10. Migração

  11. Segurança

  12. Implantação

  13. Orquestração

Criei uma apresentação bem simples comentando sobre algumas ferramentas que veremos ao longo dessa série sobre microservices, a apresentação esta pública e disponível para visualização através deste link.

Para facilitar os seus estudos e o entendimento sobre todos os posts que serão abordados, eu desenvolvi alguns projetos utilizando todas as ferramentas mencionadas na apresentação, então aproveita e dá uma olhada nos fontes, eles estão hospedados nesse repositório no github.

Roteamento dinâmico

Para realizar o roteamento dinâmico das requisições aos nossos serviços, vamos precisar entender o básico sobre como funciona um servidor de proxy reverso e um gateway.

Proxy Reverso

Um proxy reverso repassa o tráfego de rede recebido para um conjunto de servidores, tornando-o a única interface para as requisições externas. Por exemplo, um proxy reverso pode ser usado para balancear a carga de um cluster de servidores Web.

Gateway

Em termos simples, o Gateway é um proxy reverso aprimorado com recursos mais avançados, incluindo orquestração e segurança adicional e recursos de monitoramento.

Spring Cloud Gateway

Spring Cloud Gateway é a implementação do API Gateway desenvolvida pela equipe do Spring Cloud em cima do ecossistema reativo da Spring Framework. Ele fornece uma maneira simples e eficaz de encaminhar solicitações recebidas para o destino apropriado usando o Gateway Handler Mapping.

E o Spring Cloud Gateway como mencionado é reativo, portanto ele faz o uso do servidor Netty para fornecer processamento de solicitação assíncrona não bloqueantes.

Abaixo está o fluxo de alto nível de como o roteamento de solicitações funciona no Spring Cloud Gateway:

1

Spring Cloud Gateway consiste em 3 blocos principais de construção:

Routing: O bloco básico de construção do gateway. É definido por um ID, uma URI de destino, uma lista de predicates e de filters. Como uma rota pode ter uma lista de predicates a requisição será passada ao serviço somente se todo agregado dos predicates forem verdadeiros.

Predicate: Utilizando Predicate do pacote de funções do Java 8 podemos combinar validações, e através do objeto ServerWebExchange temos acesso as informações do cabeçalho e parâmetros da requisição, com isso conseguimos aplicar diversas validações, fazendo com que a requisição chegue ao nosso serviço somente se as regras forem atendidas.

Filter: Ao implementar a interface GatewayFilter, podemos modificar as respostas das requisições dos serviços.

Criando nosso servidor

Vamos criar um novo projeto utilizando o Spring Boot, para isso acesse o endereço https://start.spring.io/ e adicione as seguintes dependências Config Client, Eureka Discovery Client e Gateway, já as demais configurações ficam a sua escolha.

2

Após adicionarmos as dependências podemos gerar o nosso projeto. Assim que o download for concluído, descompacte o diretório contendo o projeto e acesse os fontes na sua IDE preferida.

Configurando o projeto

Para configuração deste serviço vamos utilizar o Config Server que desenvolvemos no primeiro post, mas para isso devemos definir um nome para a nossa nova aplicação e adicionar uma simples configuração apontando para o link do Config Server. Todas essas definições devem ser adicionadas no arquivo bootstrap.properties, pois este arquivo é responsável pelo carregamento de configurações externas em projetos Spring Boot, e deve ser adicionado dentro da pasta resources do nosso projeto.

spring.application.name=gateway
spring.cloud.config.uri=${CONFIG_SERVER_URL:http://localhost:8888}
spring.cloud.config.fail-fast=true

Segue uma breve explicação sobre as configurações adicionadas:

  • Spring.application.name: Definimos o nome da nossa aplicação.

  • Spring.cloud.config.uri: Apontamos para o endereço do Config Server.

  • Spring.cloud.config.fail-fast: Especificamos que caso o Config Server não responda a nossa aplicação deve lançar uma exceção e falhar.

Agora que já sabemos o nome do nosso serviço e também já configuramos a utilização do Config Server, devemos criar um arquivo yaml com o mesmo nome atribuído ao serviço, que neste caso será chamado de gateway.yaml, este arquivo deve ser adicionado dentro do repositório git configurado no Config Server, e deve ter o seguinte conteúdo.

server:
  port: 8080

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true

Observe que utilizamos yaml para configuração e não mais um arquivo properties, esses são os dois tipos de arquivos suportados para configurações. Agora vamos entrar em detalhes nas configurações atribuídas.

  • server.port: Porta em que o servidor http vai responder as requisições.

  • spring.cloud.gateway.discovery.locator.enabled: Esta propriedade é que faz toda a magica acontecer, pois o gateway vai obter a lista de serviços registrados no service discovery que em nosso caso é o Eureka, e vai configurar uma rota dinâmica para todos os serviços que estão registados no Eureka, também vai realizar o balanceamento de carga nas requisições.

  • spring.cloud.gateway.discovery.locator.lower-case-service-id: Como não existe um padrão definido para registro de nomes dos serviços, através desta propriedade conseguimos especificar que o nome das rotas para os serviços devem ter o nome todo em minusculo.

Como todos os novos serviços devem se registrar no Eureka, vamos adicionar as seguintes configurações no arquivo application.properties, este arquivo fica dentro do repositório git configurado no Config Server. Lembrando que todas as configurações que ficam no arquivo application.properties são compartilhada entre todos os serviços, portanto o nosso serviço de gateway vai receber todas as configuração do arquivo yaml criado acima e do properties escrito abaixo.

eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.preferIpAddress=true

Segue uma breve explicação sobre as configurações adicionadas:

  • eureka.client.serviceUrl.defaultZone: Definimos o endereço do Eureka Server para que nossa aplicação consiga se registrar.

  • eureka.instance.preferIpAddress: Como nossa aplicação vai rodar localmente queremos que os endereços de ips das maquinas sejam registradas no Eureka e não o seu host name.

Agora vamos configurarmos as regras do CORS (Cross-Origin Resource Sharing) para permitir que paginas web de outros domínios realizem requisições ao nosso servidor. Para isso crie um pacote chamado de config e crie a classe a seguir dentro deste novo pacote.

import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;

@Configuration
@EnableWebFlux
public class CorsGlobalConfiguration implements WebFluxConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**")
          .allowedOrigins("*")
          .allowedMethods("POST", "PUT", "GET", "OPTIONS", "DELETE", "PATCH")
          .allowedHeaders("Origin", "Authorization", "Content-Type", "Accept", "X-CSRF-TOKEN")
          .allowCredentials(true)
          .maxAge(3600);
    }

}

A classe acima é auto explicativa, ela permite com que o nosso servidor http receba requisições de todas as origens, nos verbos http listados acima e também permite determinados parâmetros no cabeçalho da requisição.

Executando o serviço

Com tudo configurado agora podemos iniciar nossa aplicação, para isso execute o método main da classe GatewayApplication, ou gere o artefato da aplicação utilizando o seguinte comando do maven mvn package e depois acesse a pasta target pelo terminal e execute o seguinte comando java -jar gateway-4.0.jar, observe o número da versão no final do artefato, no meu repositório do github defini o Gateway com a versão 4, mas caso tenha criado um novo projeto utilize a versão que está declara no arquivo pom.xml.

Testando o serviço

Vamos acessar o seguinte endereço no navegador http://localhost:8761/.

3

Agora temos o primeiro serviço registrado no Eureka, observe a linha em vermelho, o nome do serviço registrado é gateway, exatamente como definimos no arquivo de configuração bootstrap.properties.

Resumo

Neste post criamos um serviço que será o nosso Gateway, é através dele que os clientes poderão realizar chamadas as nossas API’s, pois nosso gateway se comunica com o Eureka, obtendo todos os serviços registrados e cria uma rota dinâmica para esses serviços, até mesmo realiza o balanceamento de carga das requisições. Então fiquem ligados que no próximo post vamos construir um micro serviço responsável por cadastrar produtos em uma base de dados, esse serviço vai expor sua API por meio do nosso Gateway, ele também vai se registrar no Eureka e utilizar o Config Server para se auto configurar.

// Compartilhe esse Post