Spring Cloud Series - o padrão de gateway

Spring Cloud Series - o padrão de gateway

1. Visão geral

So far, em nosso aplicativo de nuvem, usamos o padrão de gateway para oferecer suporte a dois recursos principais.

Primeiro, isolamos nossos clientes de cada serviço, eliminando a necessidade de suporte entre origens. Em seguida, implementamos a localização de instâncias de serviços usando o Eureka.

Neste artigo, veremos como usar o padrão Gateway pararetrieve data from multiple services with a single request. Para fazer isso, vamos apresentar Feign em nosso gateway para ajudar a escrever as chamadas de API para nossos serviços.

Para ler sobre como usar o cliente Feign, verifiquethis article.

O Spring Cloud agora também fornece o projetoSpring Cloud Gateway que implementa esse padrão.

2. Configuração

Vamos abrir opom.xml de nosso servidorgateway e adicionar a dependência para Feign:


    org.springframework.cloud
    spring-cloud-starter-feign

Para referência - podemos encontrar as versões mais recentes emMaven Central (spring-cloud-starter-feign).

Agora que temos o suporte para construir um cliente Feign, vamos habilitá-lo noGatewayApplication.java:

@EnableFeignClients
public class GatewayApplication { ... }

Agora vamos configurar os clientes do Feign para os serviços de livro e classificação.

3. Feign Clients

3.1. Cliente de livro

Vamos criar uma nova interface chamadaBooksClient.java:

@FeignClient("book-service")
public interface BooksClient {

    @RequestMapping(value = "/books/{bookId}", method = RequestMethod.GET)
    Book getBookById(@PathVariable("bookId") Long bookId);
}

Com essa interface, estamos instruindo o Spring a criar um cliente Feign que acessará o endpoint “/books/\{bookId}”. Quando chamado, o métodogetBookById fará uma chamada HTTP para o ponto de extremidade e usará o parâmetrobookId.

Para fazer este trabalho, precisamos adicionar um DTOBook.java:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Book {

    private Long id;
    private String author;
    private String title;
    private List ratings;

    // getters and setters
}

Vamos passar paraRatingsClient.

3.2. Cliente de classificações

Vamos criar uma interface chamadaRatingsClient:

@FeignClient("rating-service")
public interface RatingsClient {

    @RequestMapping(value = "/ratings", method = RequestMethod.GET)
    List getRatingsByBookId(
      @RequestParam("bookId") Long bookId,
      @RequestHeader("Cookie") String session);

}

Como comBookClient, o método exposto aqui fará uma chamada de descanso para nosso serviço de classificação e retornará a lista de classificações de um livro.

No entanto, esse ponto de extremidade está protegido. To be able to access this endpoint properly we need to pass the user’s session to the request.

Fazemos isso usando a anotação@RequestHeader. Isso instruirá Feign a gravar o valor dessa variável no cabeçalho da solicitação. Em nosso caso, estamos escrevendo no cabeçalhoCookie porque o Spring Session estará procurando por nossa sessão em um cookie.

Em nosso caso, estamos escrevendo no cabeçalhoCookie porque o Spring Session estará procurando nossa sessão em um cookie.

Finalmente, vamos adicionar um DTORating.java:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Rating {
    private Long id;
    private Long bookId;
    private int stars;
}

Agora, os dois clientes estão completos. Vamos colocá-los em uso!

4. Pedido Combinado

Um caso de uso comum para o padrão Gateway é ter pontos de extremidade que encapsulam os serviços geralmente chamados. Isso pode aumentar o desempenho, reduzindo o número de solicitações do cliente.

Para fazer isso, vamos criar um controlador e chamá-lo deCombinedController.java:

@RestController
@RequestMapping("/combined")
public class CombinedController { ... }

Em seguida, vamos conectar nossos clientes falsos recém-criados:

private BooksClient booksClient;
private RatingsClient ratingsClient;

@Autowired
public CombinedController(
  BooksClient booksClient,
  RatingsClient ratingsClient) {

    this.booksClient = booksClient;
    this.ratingsClient = ratingsClient;
}

E, finalmente, vamos criar uma solicitação GET que combina esses dois endpoints e retorna um único livro com suas classificações carregadas:

@GetMapping
public Book getCombinedResponse(
  @RequestParam Long bookId,
  @CookieValue("SESSION") String session) {

    Book book = booksClient.getBookById(bookId);
    List ratings = ratingsClient.getRatingsByBookId(bookId, "SESSION="+session);
    book.setRatings(ratings);
    return book;
}

Observe que estamos definindo o valor da sessão usando a anotação@CookieValue que o extrai da solicitação.

Aí está! Temos um endpoint combinado em nosso gateway que reduz as chamadas de rede entre o cliente e o sistema!

5. Teste

Vamos verificar se nosso novo endpoint está funcionando.

Navegue atéLiveTest.javae vamos adicionar um teste para nosso endpoint combinado:

@Test
public void accessCombinedEndpoint() {
    Response response = RestAssured.given()
      .auth()
      .form("user", "password", formConfig)
      .get(ROOT_URI + "/combined?bookId=1");

    assertEquals(HttpStatus.OK.value(), response.getStatusCode());
    assertNotNull(response.getBody());

    Book result = response.as(Book.class);

    assertEquals(new Long(1), result.getId());
    assertNotNull(result.getRatings());
    assertTrue(result.getRatings().size() > 0);
}

Inicie o Redis e execute cada serviço em nosso aplicativo:config, discovery, zipkin,gateway,book e o serviçorating.

Quando tudo estiver pronto, execute o novo teste para confirmar que está funcionando.

6. Conclusão

Vimos como integrar o Feign em nosso gateway para construir um endpoint especializado. Podemos aproveitar essas informações para criar qualquer API que precisamos oferecer suporte. Mais importante, vemos que não estamos presos a uma API de tamanho único que expõe apenas recursos individuais.

Usando o padrão de gateway, podemos configurar nosso serviço de gateway para as necessidades de cada cliente de maneira única. Isso cria dissociação, dando aos nossos serviços a liberdade de evoluir conforme necessário, permanecendo enxutos e focados em uma área do aplicativo.

Como sempre, trechos de código podem ser encontradosover on GitHub.