Spring Cloud Series - Le modèle de passerelle

Spring Cloud Series - Le modèle de passerelle

1. Vue d'ensemble

So far, dans notre application cloud, nous avons utilisé le modèle de passerelle pour prendre en charge deux fonctionnalités principales.

Premièrement, nous avons isolé nos clients de chaque service, éliminant ainsi le besoin d’une prise en charge multi-origine. Ensuite, nous avons implémenté la localisation d’instances de services utilisant Eureka.

Dans cet article, nous allons voir comment utiliser le modèle de passerelle versretrieve data from multiple services with a single request. Pour ce faire, nous allons introduire Feign dans notre passerelle pour aider à écrire les appels d'API à nos services.

Pour savoir comment utiliser le client Feign, consultezthis article.

Spring Cloud fournit désormais également le projetSpring Cloud Gateway qui implémente ce modèle.

2. Installer

Ouvrons lespom.xml de notre serveurgateway et ajoutons la dépendance pour Feign:


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

Pour référence - nous pouvons trouver les dernières versions surMaven Central (spring-cloud-starter-feign).

Maintenant que nous avons le support pour créer un client Feign, activons-le dans lesGatewayApplication.java:

@EnableFeignClients
public class GatewayApplication { ... }

Configurons maintenant les clients Feign pour les services de livres et de notation.

3. Feindre les clients

3.1. Client de réservation

Créons une nouvelle interface appeléeBooksClient.java:

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

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

Avec cette interface, nous demandons à Spring de créer un client Feign qui accédera au point de terminaison «/books/\{bookId}». Lorsqu'elle est appelée, la méthodegetBookById effectuera un appel HTTP vers le point de terminaison et utilisera le paramètrebookId.

Pour que cela fonctionne, nous devons ajouter un DTOBook.java:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Book {

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

    // getters and setters
}

Passons auxRatingsClient.

3.2. Client d'évaluation

Créons une interface appeléeRatingsClient:

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

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

}

Comme pour lesBookClient, la méthode exposée ici fera un appel de repos à notre service de notation et retournera la liste des notes pour un livre.

Cependant, ce noeud final est sécurisé. To be able to access this endpoint properly we need to pass the user’s session to the request.

Nous faisons cela en utilisant l'annotation@RequestHeader. Cela demandera à Feign d'écrire la valeur de cette variable dans l'en-tête de la requête. Dans notre cas, nous écrivons dans l'en-têteCookie car Spring Session recherchera notre session dans un cookie.

Dans notre cas, nous écrivons dans l'en-têteCookie car Spring Session recherchera notre session dans un cookie.

Enfin, ajoutons un DTORating.java:

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

Maintenant, les deux clients sont complets. Mettons-les à profit!

4. Demande combinée

Un cas d'utilisation courant pour le modèle de passerelle consiste à avoir des points de terminaison qui encapsulent des services couramment appelés. Cela peut augmenter les performances en réduisant le nombre de demandes de clients.

Pour ce faire, créons un contrôleur et appelons-leCombinedController.java:

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

Ensuite, transmettons nos clients fictifs nouvellement créés:

private BooksClient booksClient;
private RatingsClient ratingsClient;

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

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

Et enfin, créons une requête GET qui combine ces deux points de terminaison et renvoie un seul livre avec ses notes chargées:

@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;
}

Notez que nous définissons la valeur de session à l'aide de l'annotation@CookieValue qui l'extrait de la requête.

Le voilà! Nous avons dans notre passerelle un terminal combiné qui réduit les appels réseau entre le client et le système!

5. Essai

Assurons-nous que notre nouveau point de terminaison fonctionne.

Accédez àLiveTest.java et ajoutons un test pour notre point de terminaison combiné:

@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);
}

Démarrez Redis, puis exécutez chaque service dans notre application:config, discovery, zipkin,gateway,book et le servicerating.

Une fois que tout est en place, lancez le nouveau test pour confirmer qu'il fonctionne.

6. Conclusion

Nous avons vu comment intégrer Feign dans notre passerelle pour créer un point de terminaison spécialisé. Nous pouvons exploiter ces informations pour créer toute API à prendre en charge. Plus important encore, nous constatons que nous ne sommes pas piégés par une API unique qui n'expose que des ressources individuelles.

En utilisant le modèle de passerelle, nous pouvons configurer notre service de passerelle en fonction des besoins de chaque client de manière unique. Cela crée un découplage qui donne à nos services la liberté d’évoluer au gré de leurs besoins, tout en restant sobres et centrés sur un domaine de l’application.

Comme toujours, des extraits de code peuvent être trouvésover on GitHub.