BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Artigos Desmistificando o Spring Cloud Netflix

Desmistificando o Spring Cloud Netflix

A Spring Cloud possui uma stack completa de microservices, que foi construída e é mantida pela Pivotal e lançou sua primeira versão em 2014, e é frequentemente atualizada.O objetivo principal do Spring Cloud é fornecer uma integração completa entre o Spring Boot e o projeto Netflix OSS. Netflix OSS é um conjunto de frameworks e bibliotecas que a Netflix escreveu para resolver alguns problemas comuns em sistemas distribuídos em escala. Hoje o Netflix OSS é sinônimo de desenvolvimento de microservices em um ambiente em nuvem, com uma anotação simplificada é possível ter alguns componentes utilizados pelo Netflix em execução no seu ambiente.

A integração entre o Netflix OSS e o Spring Boot fornece configurações simples de aplicativos com todos os benefícios do ambiente de desenvolvimento do Spring.

Com as anotações é possível ativar e configurar rapidamente os padrões comuns dentro de um aplicativo e criar grandes sistemas distribuídos, com componentes Netflix testados e utilizados pela própria empresa.

Alguns padrões do Netflix OSS que o Spring Cloud fornece:

  • Service Discovery (Eureka)
  • Circuit Breaker (Hystrix/Turbine)
  • Intelligent Routing (Zuul)
  • Client Side Load Balancing (Ribbon)

Neste artigo, discutiremos a service discovery, service registration e circuit breaker, mostrando o conceito e como é possível, com algumas anotações, ter todas essas ferramentas com Spring Boot.

Todos esses componentes só fazem sentido se você usar uma arquitetura de Microservices.

Por que?

Porque a característica de um microservice, é baseada na decomposição de serviços, onde cada serviço pode ser considerado micro, pois cada pequeno recurso que trabalha em conjunto oferece uma ótima característica.

Tudo isso é muito baseado em domain driven design(DDD), interfaces bem definidas, princípios de single responsibility e SOA.

Toda esta decomposição dá agilidade, flexibilidade, escalabilidade e ajuda o negócio a evoluir de acordo com suas necessidades.

Então, começamos agora com um conceito de que, em vez de ter um único serviço, agora temos muitos serviços pequenos e provavelmente vamos replicar esses serviços.

Características de Microservices:

  • Domain Driven Design
  • Single Responsibility Principle
  • Interface Pública e Explícita
  • Deploy Independente
  • Light-weight Communication

Algumas vantagens de Microservices:

  • Mais fácil de desenvolver, compreender e manter
  • Começa mais rápido do que um monolito
  • Mudanças locais podem ser facilmente implantadas
  • Escalar de forma independente
  • Melhora o isolamento de falhas

Quando temos um ou dois serviços, ok, é fácil identificar onde os serviços, mas se temos 20 ou 30, em muitas máquinas, como saberemos se eles estão disponíveis ou não?

Ao trabalhar com microservices, começamos a ter inúmeros problemas que não tínhamos antes.

Portanto, houve alguns padrões que começamos a ter que prestar atenção, o que é o caso de Service Discovery, Service Register e Circuit Breaker, isso deixa mais fáceis algumas tarefas.

Segundo Arun Gupta um conceito que pode ser utilizado para microservices é:

O Microservices pode parecer uma bala de prata que pode resolver uma quantidade significativa de problemas de software. Eles servem um bom propósito, mas certamente não são fáceis.

Service Discovery

O Service Discovery é um dos principais princípios da arquitetura baseada em microservices. Tentando configurar a mão para cada cliente ou alguma forma de convenção pode ser muito difícil de fazer e pode ser muito frágil.

O Eureka é o Service Discovery do Netflix que é usado no Server e no Cliente.

O servidor pode ser configurado e implantado para estar altamente disponível, com cada servidor replicando o estado sobre os serviços registrados para os outros.

Por quê Service Discovery?

Vamos imaginar que temos muitos serviços dinamicamente distribuídos na rede. Onde as instâncias de serviços mudam dinamicamente devido a escala automática, falhas, atualizações e não temos controle de endereços IP e nem o nome da instância.

O ideal nessa situação seria que o serviço comunica-se ao servidor ou até mesmo a algum serviço que poderia chamá-lo que está disponível para ser requisitado.

É assim que o Service Discovery funciona, o serviço se juntou à rede e se registrou em um servidor para disponibilizá-lo para o uso de algum outro serviço.

O padrão que será abordado nesta publicação será o The Server-Side Pattern Discovery.

Onde os clientes sabem onde é o servidor e enviam para o servidor que está disponível.

Registrando

Quando um cliente se registra no Eureka, fornece metadados sobre si próprio, como host e porta,health check , URL indicador, página inicial, etc.

O Eureka recebe mensagens de heartbeat de cada instância que pertencente a um serviço. Se o heartbeat falhar ao longo de um cronograma configurável, a instância normalmente é removida do registro.

Página de Status e Health Indicator

A localização da rede de uma instância de serviço é registrada no registry service quando ele é iniciado.

Quando é removido registry service quando uma instância ou o serviço não está mais disponível.

Esse mecanismo de saber se um serviço está disponível ou não é chamado de heartbeat.

Health Checks do Eureka

Por padrão, a Eureka usa os heartbeat do cliente para determinar se um cliente está ausente. A menos que especificado de outra forma, o Discovery Client não irá propagar o status de verificação de estado atual do aplicativo pelo Spring Boot Actuator. O que significa que após o registro bem sucedido, a Eureka sempre anunciará que o aplicativo está no estado 'UP'. A habilitação do health check do Eureka pode alterar esse comportamento, o que resulta na propagação do status do aplicativo para a Eureka. Como conseqüência, qualquer outro aplicativo não enviará tráfego para o aplicativo em outros estados 'UP'.

Circuit Breaker Pattern

Quando se trabalha com microservices, chamadas remotas em diferentes tipos de de sistemas é uma das atividades mais comuns neste cenário.

Provavelmente, esses sistemas vão estar em diferentes máquinas distribuídas por uma rede.

Mas o que acontece quando uma chamada para um desses sistemas falha ou tem uma resposta em um tempo inadequado? O sistema pode falhar como um todo e o problema pode ser conectado em cascata a outros sistemas que dependem dessa solicitação.

Para evitar esse tipo de problema, existe um padrão chamado Circuit Breaker. A idéia é simples, exatamente como um Circuit Breaker (Disjuntor) de uma casa. Você projeta uma chamada remota para um objeto Circuit Breaker, que monitora falhas. Quando essas falhas chegam a um certo limite, o circuit breaker dispara, e quando desarmar uma ação pode ser colocada em caso de falha ou até mesmo um aviso que o circuit breaker desarmou.

Alguns conceitos importantes do padrão circuit breaker

Closed: o Request do aplicativo é encaminhado para a operação. O proxy mantém uma contagem do número de falhas recentes, e se a chamada para a operação não tiver êxito, o proxy incrementa essa contagem. Se o número de falhas recentes exceder um limiar especificado dentro de um determinado período de tempo, o proxy é colocado no estado Open. Neste ponto, o proxy inicia um temporizador de tempo limite e, quando esse temporizador expirar, o proxy é colocado no estado Half-Open.

Open: a Request do aplicativo falha imediatamente e uma exceção é retornada para o aplicativo

Half-Open: um número limitado de pedidos do aplicativo pode passar e invocar a operação. Se esses pedidos forem bem-sucedidos, presume-se que a falha que anteriormente causou a falha foi corrigida e o circuit breaker muda para o estado Closed (o contador de falhas é reiniciado). Se algum pedido falhar, o circuit breaker assume que a falha ainda está presente para que ele volte para o estado aberto e reinicia o temporizador de tempo limite para dar ao sistema um período de tempo adicional para se recuperar da falha.

Quando usar este padrão

  • Para evitar que um aplicativo tente invocar um serviço remoto ou acessar um recurso compartilhado se esta operação for altamente provável que falhe.

Este padrão pode não ser adequado

  • Para processar o acesso a recursos privados locais em um aplicativo, como a estrutura de dados na memória. Neste ambiente, o uso de um circuit breaker simplesmente adicionaria sobrecarga ao seu sistema.
  • Como substituto para lidar com exceções na lógica de negócio a seus aplicativos.

O Netflix criou uma ferramenta que implementa o Circuit Breaker chamada de Hystrix e Spring Cloud facilitou ainda mais a implementação deste padrão.

A idéia desse artigo é apresentar um exemplo completo de Eureka Server, utilizar o circuit breaker com Hystrix, e criar um dashboard com Turbine. Cada uma dessas partes com exemplo de código. O conteúdo completo desse exemplo pode ser encontrado em meu GitHub.

O primeiro componente para este exemplo é o eureka. Nos trechos de código listados logo abaixo, fazemos a configuração com o SpringBoot para iniciar o eureka através da classe ApplicationEurekaServer e as configurações necessárias no arquivo application.yml.

ApplicationEurekaServer.java

@SpringBootApplication
@EnableEurekaServer
public class ApplicationEurekaServer {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ApplicationEurekaServer.class)
        	.web(true)
        	.run(args);
    }
}
application.yml
#Server Specifics
server:
  port: 8761

error:
    whitelabel:
      enabled: false

spring:
  application:
    name: eureka-server

#Eureka Specifics

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

Este componente deve ser o primeiro a ser executado e estará disponível em http://localhost:8761. Ao acessar o endereço, esta tela pode ser vista:

Finalizado o servidor Eureka, o próximo componente será o serviço com o cliente eureka chamado recommend-service.

Este serviço tem duas operações que fornecem o nome do livro, quando comprar, e outro para recomendar.

A classe BookstoreApplication tem uma operação simples de livros a serem recomendados. Nessa classe também colocamos as anotações de eureka client para conectar no eureka server e habilitamos o hystrix para iniciar o circuit breaker . As configurações do serviço ficam armazenadas no arquivo application.yml, onde será informada a URL do servidor Eureka a ser utilizado. Por fim, no arquivo bootstrap.yml colocamos a configuração para identificar a aplicação.

BookstoreApplication.java

@RestController
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class BookstoreApplication {

  @RequestMapping(value = "/recommended")
  public String readingList(){
    return "Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)";
  }
  
  @RequestMapping(value = "/buy")
  public String buy(){
    return "Java 8 in Action";
  }

  public static void main(String[] args) {
    SpringApplication.run(BookstoreApplication.class, args);
  }
}
application.yml

eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
bootstrap.yml

spring:
  application:
    name: recommended

O terceiro componente é o Serviço com Hystrix / Circuit Breaker. Este serviço é chamado de read-service, nesse serviço será verificado se o serviço dependente (recommend-service) está disponível, e verificar se o circuito está aberto ou fechado.

Existem duas operações: /to-read e /to-buy. Este serviço também possui configuração para Hystrix Dashboard e Eureka Client e estará disponível em http://localhost:8080.

ReadingApplication.java

@EnableCircuitBreaker
@RestController
@SpringBootApplication
@EnableHystrixDashboard
@EnableEurekaClient
public class ReadingApplication {

	@Autowired
	private BookService bookService;

	@RequestMapping("/to-read")
	public String toRead() {
		return bookService.readingList();
	}
	
	@RequestMapping("/to-buy")
	public String toBuy() {
		return bookService.buy();
	}

	public static void main(String[] args) {
		SpringApplication.run(ReadingApplication.class, args);
	}
}

BookService.java

@Service
public class BookService {

	@Bean
	RestTemplate restTemplate() {
		return new RestTemplate();
	}

	@Autowired
	RestTemplate restTemplate;

	@Autowired
	@Lazy
	private EurekaClient discoveryClient;

	@HystrixCommand(fallbackMethod = "reliable")
	public String readingList(){
		return this.restTemplate.getForObject(URI.create(serviceUrl() + "recommended"), String.class);
	}

	public String serviceUrl() {
		InstanceInfo instance = discoveryClient.getNextServerFromEureka("RECOMMENDED", false);
		return instance.getHomePageUrl();
	}

	public String reliable() {
		return "Cloud Native Java (O'Reilly)";
	}
	
	@HystrixCommand(fallbackMethod = "reliableBuy")
	public String buy(){
		return this.restTemplate.getForObject(URI.create(serviceUrl() + "buy"), String.class);
	}

	public String reliableBuy() {
		return "Any Java 8 Book";
	}
}

Nesta imagem, a anotação @HystrixCommand é a configuração para o circuit breaker, se algo de errado acontecer, o método reliable será chamado, o mesmo no segundo @HystrixCommand, se algo errado acontecer, o método realiableBuy será chamado.

Os serviços são chamados através do Eureka Client. O conceito é perguntar ao Eureka Server o endereço para chegar ao recommended-service, por exemplo.

application.yml

eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
bootstrap.yml

spring:
  application:
    name: read

O último componente é Turbine é uma ferramenta para eventos agregados produzidos na Hystrix. 

Imaginemos que temos um cluster com quase 10 read-service, cada um com Hystrix, é difícil monitorar todos os circuitos. Neste momento, Turbine funciona, para fornecer agregação aos Circuitos Breakers.

Netflix usa Hystrix que possui um painel de controle em tempo real que usa Turbine para agregar dados de 100s ou 1000s de máquinas.

No nosso exemplo, o turbine-service estará disponível em http://localhost:8989.

TurbineApplication.java

@SpringBootApplication
@EnableEurekaClient
@EnableHystrixDashboard
@EnableTurbine
public class TurbineApplication {
	public static void main(String[] args) {
		SpringApplication.run(TurbineApplication.class, args);
	}
}
application.yml

server:
  port: 8989

spring:
  application:
    name: turbine
    
turbine:
  aggregator:
    clusterConfig: READ
  appConfig: READ

logging:
  level: INFO
  
eureka:
  client:
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

Nesta imagem, o cluster config aqui significa o serviço que tem @HystrixCommand, devemos colocar o nome do serviço registrado em Eureka = READ

Depois de todos esses componentes iniciados no Eureka Server http://localhost:8761 é possível ver 3 instâncias registradas.

Neste link http://localhost:8080/hystrix estará disponível o Hystrix Dashboard.

Informe o Single Hystrix http://localhost:8080/hystrix.stream

Esta tela abaixo está aguardando solicitação para construir o circuito

Primeiro request: http://localhost:8080/to-read

Segundo request http://localhost:8080/to-buy

Nesta imagem, o circuito está fechado.

Para abrir Turbine é o mesmo passo que Hystrix, devo informar o Cluster via Turbine: http://localhost:8989//turbine.stream?Cluster=READ

A mesma tela Hystrix será aberta, mas se eu tiver mais serviço, ela aparecerá de forma agregada.

Agora desliguei o serviço de recomendação. A configuração padrão é 20 solicitação para considerar o circuito aberto, eu fiz 20 vezes esse pedido http://localhost:8080/to-buy

O resultado

A idéia nesta publicação foi mostrar como usar algumas ferramentas consolidadas pelas grandes empresas e quais os problemas que eles resolveram ao lidar com o Microservice.

Mais importante do que saber a ferramenta é saber o problema que resolve e se existe algum padrão para resolver nosso problema.

Sobre o autor:

Atualmente Rafael Salerno de Oliveira trabalha como Arquiteto de Software na ilegra em Porto Alegre, RS. Ele possui mais de 12 anos de experiência trabalhando com desenvolvimento, design, arquitetura de software e devops engineer. Nos últimos anos vem trabalhando em diferentes projetos incentivando a utilização de Programação Funcional, eXtreme Programming, Kanban, Arquitetura Evolucionária, Integração Contínua, Entrega Contínua, TDD, SOA, Microservices e Cloud Platform.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT