A propos de moi

images/daniel/about-me.png

Les applications distribuées modernes ?

Aujourd’hui, le mot d’ordre est livraison rapide, haute disponibilité et scalabilité.

Secret de la livraison rapide

Quelques pistes pour la haute disponibilité

Bref…

Des microservices !

images/solutions/microservices.png

Un exemple

images/architecture/classic-stack-example.png

Au microscope cette fois-ci

images/architecture/sc-stack-example.png

Pourquoi un déploiement multi instance ?

Du failover !!!

La vie est imprévisible ! Alors autant faire un backup ! Un backup c’est bien, mais un système actif / actif, c’est mieux ! Le premier reflexe est donc d’avoir un minimum de deux instances pour offrir un service.

Le secret résidera donc dans la capacité à ces services à répondre aux requêtes qui leur sont addressé de manière transparente pour les clients.

La réponse dans un service RESTfull !

Un service sans état constitue la solution la plus adapté pour répartir une charge sur plusieurs instance. Si la requête addressé à un service est sans état, nous avons la garantie que n’importe qui peut prétendre y répondre.

La voie ouverte pour du Load Balancing

Une fois que nous avons la certitude que les services de notre architectures sont stateless, rien ne nous empêche de nous lancer avec des solutions de Load Balacing. Un répartiteur de charge assura que notre plateforme pourra scaler à volonté sans contrainte. Attention, ceci se limite à la couche applicative. Les systèmes de persistences utilisé par la plateforme doivent eux aussi être pensé pour scaler à leur manière.

Système distribué = système compliqué = Routage !

Inévitablement, une plateforme orienté micro service finit par englober une myriade de service éparpillé partout. Ultimement, certains de ces services ont pour but d'être exposé à des tiers. Par conséquent, il faut offrir un mécanisme d’abstraction de l’architecture à ces tiers. Un client ne doit pas et ne peut pas connaître la topologie physique des services déployés.

images/problems/routage-1.png

Routage = Flexibilité

images/architecture/zuul.png

Et la haute disponibilité c’est gratuit ?

Et bien non ! Eclater un service à travers le réseau n’a pas que des avantages. Une architecture orienté micro service est certe plus élastique et maléable, cependant elle introduit des contraintres pour lesquels des solutions sont nécessaire.

Un petit rappel

images/problems/monolith-microservices.jpg

Les dangers des microservices

images/problems/microservice-problems.png

Sans Service Discovery

Imaginons une platforme de vente avec deux modules nécessitant de communiquer entre eux.

images/problems/service-discovery-1.png

Avec cette configuration, chaque instance de service doit connaitre les adresses permettant de rejoindre ses dépendances. Très rapidement, ce modèle ne tient pas la route. Spécialement dans un context où des instances sont déployées à la demande.

Avec Service Discovery

La solution pour ce type de problème consiste à mettre à disposition un registre de service hautement disponible. Chaque instance de la plateform peut donc contacter le composant Service Discovery pour s’enregistrer et récupérer les adresses de tous les services composant la plateforme. Les applications clientes d’un registre de services doivent donc être penser pour s’adapter dynamiquement aux adresses de leur dépendances.

images/problems/service-discovery-2.png

1 - Enregistrement au registre de service.

2 - Communications avec les services reçus du registre.

Configuration Management

Derrière le problème solutionné par le Service Discovery se cache un problème fondamentalement plus large. Il s’agit de la gestion de configuration.

Sur des applications legacy, les configurations sont généralement gérées dans des fichiers. Encore une fois, une gestion manuelle ne permet pas de de scaler l’application. Des outils de provisionning tel que Puppet ou Chef peuvent se présenter comme une solution mais ne présentent pas la plus élégante.

Sans gestion de configuration centralisé

Sur ce type de système, chaque service porte lui même sa configuration. Chaque évolution de configuration nécessite donc de mettre à jour tous ses fichiers ou encore de redéployer les fichiers à travers les outils de provisionning.

Pas très pratique lorsqu’on parle d’application cloud.

images/problems/configuration-management-1.png

Avec gestion de configuration centralisé

La meilleur pratique qui soit dans ce domaine se traduit par l’utilisation d’un service de configuration. Ce service est responsable d’héberger les configurations et de les rendre accessible aux différents services de la plateforme.

Ainsi, la seule configuration nécessaire à déployer en dur pour chaque service concerne les instructions pour communiquer avec le service de configuration.

images/problems/configuration-management-2.png

Load Balancing

Dans un système distribué, on retrouve systématiquement plusieurs instances pour un unique service. Avant même de parler de scalabilité, l’argument premier restera la redondance des services. Deux approches sont possibles pour gérer un Load Balancing.

Reverse Proxy

Le service discovery et la gestion centralisée de configuration permettent à nos composant de communiqué entre eux de manière efficace. Cependant, ces solutions impliquent une complexité et des contraintent qui ne peut être imposée aux clients externes de notre application.

Reverse Proxy

La solution réside donc dans un Reverse Proxy frontal responsable d’abstraire la complexité interne du système aux clients externes.

images/solutions/reverse-proxy.png

Circuit breaking

Do not beat a dead horse !

images/problems/beating-a-dead-horse.jpeg

Circuit breaking

Rien se sert de s’engouffrer dans une queue qui s’empille et ne répond pas.

images/problems/long-queue.jpeg

De bonnes pratiques

Messaging

Les requêtes REST fonctionnent très bien pour des demandes de Request / Reply.

Parfois, certaines fonctionnalités s’implémente naturellement mieux avec un système de notification par messagerie.

Distributed Tracing

Il faut tenir compte que les traces qui étaient historiquement centralisés dans les logs d’un unique service monolitique seront maintenant éclaté à travers tous le parc de serveurs. Il est aussi à prendre en compte que certains logs seront généré de manière aléatoire sur les différentes instances d’un même service.

Il faut donc prévoir une solution pour tracer le parcours d’une requête à travers tout le système.

ADN d’un micro service Spring Cloud

Définition d’un microservice

Microservice architectural style is an approach to develop a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms. These services are built around business capabilities and independently deployable by fully automated deployment machinery.
— James Lewis & Martin Fowler

Pourquoi Spring Boot ?

Les avantages de Spring Boot ?

Quelques modules exemples Spring Boot

Nom Description

spring-boot-starter-web

Support for développement de la pile web complête en incluant Tomcat et spring-webmvc.

spring-boot-starter-data-jpa

Support pour “Java Persistence API” en incluant spring-data-jpa, spring-orm et Hibernate.

spring-boot-starter-security

Support pour spring-security.

spring-boot-starter-data-mongodb

Support pour la base de données NoSQL MongoDB en incluant spring-data-mongodb.

Concepts Clés de Spring Boot

Préparation d’un pom.xml
  <parent>
  <artifactId>spring-boot-starter-parent</artifactId>
  <groupId>org.springframework.boot</groupId>
  <version>1.4.0.RELEASE</version>
  </parent>
Dépendances minimales
  <dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  </dependencies>

Concepts Clés de Spring Boot

Exemple complet

Concepts Clés de Spring Boot

Bootstrap de l’application

package com.invivoo.springboot.plain;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Service;

@SpringBootApplication
public class PlanApplication {
  public static void main(String[] args) {
  System.out.println(
    SpringApplication.run(PlanApplication.class, args)
    .getBean(SuperService.class)
    .ping()
  );
  }

  @Service
  public class SuperService {
  public String ping() {
    return "pong";
  }
  }
}

Packaging de l’application

Ajout du plugin maven Spring Boot

  <build>
  <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
  </build>

Build

$ mvn package

Exécution du microservice

$ java -jar target/my-app.jar

Atelier 1 - Injection de dépendances

Atelier 2 - Web Service JSON

Note
Pour aider à l’atelier
MySQL avec Docker
Scripts SQL

Atelier 3 - Persistence avec Spring Data

Atelier 4 - Client Rest

Petite synthèse sur Spring Boot

Spring Boot ne suffit-il pas ?

Spring Boot est un outil formidable permettant d’obtenir une productivité difficile à retrouver ailleurs. Naturellement, beaucoup de système basé sur les microservices ont émergés sur une base de Spring Boot.

Ces systèmes ont tous été confrontés aux problématiques des architectures distribués. Pivotal et Netflix ont donc travaillé en compération pour offrir des solutions à ces problèmes basé sur leur expérience en production.

Spring Cloud !

Spring Cloud est extension de Spring Boot offrant des solutions aux différentes problématiques que représentent les systèmes distribués (par exemple : gestion de configuration, annuaire de service, load balancing, routage, coupe circuits, etc). Le framework facilite la communication inter process tout en assurant que les différents services ne soit pas couplés autre que par le model de données.

Pour résumer, Spring Cloud facilite la communication entre applications développé avec Spring Boot.

Utiliser Spring Cloud dans une application Spring Boot

Rien de plus simple ! Il suffit de créer une application Spring Boot qui importe des dépendances Spring Cloud.

pom.xml
  ...
  <dependencyManagement>
  <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Camden.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
  </dependencies>
  </dependencyManagement>
  ...

Modules Spring Cloud

Spring Cloud Config

Une gestion de configuration centralisé se présente comme un des première problématique à laquelle nous devons répondre sur une architecture microservice.

Le projet Spring Cloud mêt donc à disposition des modules Spring Boot qui permettent construire un serveur de configuration ainsi que des clients pouvant être utilisé par des applications Spring Boot.

Spring Cloud Config Server

Le module Spring Cloud Config Server va servir d’interface HTTP à un backend de configuration. Par défaut, le Config Server utilise un backend git. Des implémentations peuvent être fourni pour supporter n’importe quel autre type de repository. Cependant, git offre des fonctionnalités naturel aux concepts suivies par le Config Server.

L’avantage du Spring Cloud Config Server résident dans les configurations chargé depuis le service sont disponible à l’injection pour l’application. Ce mécanisme s’insère nativement dans la gestion de configuration de Spring Boot. Les ConfigurationProperties et @Value peuvent donc être utilisés sans égard sur la provenance des configurations.

Quelques concepts du Config Server

Application

Un client sur Config Server doit reseigner le nom de son application avec la propriété spring.application.name. Ce nom sera utilisé dans la résolution de configuration sur le serveur.

Profile

Un profil est une information complémentaire qui se grèffe dans la demande de configuration. En pratique, le profile peut être utilisé pour spécifier l’environment de l’instance application (ex : dev, test, hom, prod).

Priorité des configurations

Le chargement des configurations servit par le serveur se fera en chargant par ordre de priorité les fichiers suivants :

  1. nom-application-profile.properties
  2. application-profile.properties
  3. nom-application.properties
  4. application.properties

Implémentation d’un Config Server

Implémenter un config server est très simple. Il s’agit d’une simple application Spring Boot avec des dépendances spécifique et sur laquelle l’annotation @EnableConfigServer a été déclaré. C’est le minimum requis pour obtenir un serveur de configuration.

Implémentation d’un Config Server

pom.xml
  ...
  <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-config-server</artifactId>
  </dependency>
  ...

Implémentation d’un Config Server

ConfigServer.java
package com.invivoo.springcloud.configserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServer {
  public static void main(String[] args) {
  SpringApplication.run(ConfigServer.class, args);
  }
}

Implémentation d’un Config Server

application.properties
server.port: 8888
spring.cloud.config.server.git.uri: file://${user.home}/config-repo

Atelier 5 - Config Server

Objectif:
Note
Pour tester votre serveur :
http://localhost:8888/atelier-5/prod/config
Note
Pour un affichage JSON formaté depuis votre navigateur :
https://jsonview.com/

Spring Cloud Config Client

A son démarrage, le client tentera de localiser un fichier bootstrap.properties dans la racine du classpath de l’application. Dans ce fichier doit figurer toutes les configurations nécessaire pour que le service puisse contacter le Config Server et récupérer ses configurations.

bootstrap.properties
spring.cloud.config.uri=    # URL du Config Server
spring.cloud.config.label=  # Branche sur laquelle les configurations
                # sont situées
spring.cloud.config.username= # Utilisateur Git
spring.cloud.config.password= # Mot de passe Git
spring.cloud.config.failFast= # Empêche le service de démarrer si à
                # 'true'.
pom.xml
  ...
  <dependencies>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
  </dependency>
  </dependencies>
  ...

Atelier 6 - Config Client

Objectif:

Spring Cloud Netflix - Eureka

Quelques caractéristiques sur le serveur Eureka :

Spring Cloud Netflix - Eureka Server

Importation des dépendances
  ...
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
  </dependency>
  ...
EurekaServer.java
  @EnableEurekaServer
  @SpringBootApplication
  public class EurekaServer {
        public static void main(String[] args) {
                SpringApplication.run(EurekaServer.class, args);
        }
  }
application.properties
server.port=8761
spring.application.name=eureka-server
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka

Atelier 9 - Serveur Eureka

Objectif:

Spring Cloud Netflix - Eureka Client

Le client Eureka mêt à notre disposition une implémentation de l’interface EurekaClient. Cette dernière sera initialisé par l’AutoConfiguration du module spring-cloud-starter-eureka.

Note
C’est possible sans Eureka ?
Spring Cloud a construit une abstraction au dessus du client Eureka. Il s’agit du DiscoveryClient. Toutes implémentations du DiscoveryClient assure une compatibilité aux autres solution de Service Discovery avec la stack Spring Cloud.

Configuration du client Eureka

Importation des dépendances
  ...
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
  </dependency>
  ...
application.properties
spring.application.name= # Le nom de l'application doit être renseigné.
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka

Atelier 8 - Consultation du service registry

Objectifs:

Spring Cloud Netflix - Spring Cloud Dashboard

Le Spring Cloud Dashboard est un projet Open Source piloté par Julien Roy.

Il propose une interface non officiel pour Eureka spécialement adapté aux applications développé avec Spring Boot qui sont enregistré sur Eureka.

Atelier 9 - Déploiement du Spring Cloud Dashboard

Objectifs:

Spring Cloud Netflix - Ribbon

Ribbon se présente comme un load balancer client. Sa particuliarité est de venir s’incruster dans le RestTemplate de Spring. Ribbon prend son intérêt lorsqu’il est utilisé avec un client Eureka. La configuration par défaut des starter Spring Cloud s’occupera de gérer pour nous la service discovery directement au sein du RestTemplate.

Dépendance Spring Cloud Ribbon
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
  </dependency>

Récupération d’un RestTemplate Ribbon

Pour une utilisation de Ribbon avec gestion du service discovery, il est nécessaire que l’application ait activé l’annotation @EnableDiscoveryClient. Une fois ce prérequis pris en compte, il suffit de déclarer un RestTemplate avec @LoadBalanced.

Instancier un client Ribbon
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
  return new RestTemplate();
}

Failover automatique sur Ribbon

Avec Spring Cloud, le retry automatique sur les différents noeuds du Load Balancer Ribbon sera possible seulement si l’application a intégré Hystrix et activé l’annotation @EnableCircuitBreaker. Voir la section Hystrix pour plus de détails.

Spring Cloud Netflix - Feign

Le code de gestion autour du RestTemplate peut rapidement devenir répétitif. Par conséquent, la librairie Feign permet d’apporter une approche déclarative à l’implémentation de client REST.

Des annotations Spring MVC ou Jersey permettent de déclarer à Feign comment il devra binder notre client aux Web Service de notre choix.

Feign utilise Ribbon, par conséquent le nom du client feign doit correspondre à un service déclarer avec Ribbon ou bien récupéré depuis un Service Discovery.

Exemple d’un client Feign
@FeignClient("PRODUCT-SERVER")
public interface ProductClient {
        @RequestMapping("/product/{ean13}")
        Product findOne(@PathVariable(name = "ean13") String ean13);
}

Pour intégrer Feign dans son projet

Dépendance pour Feign
  ...
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
  </dependency>
  ....
Activation de Feign
@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
public class InvoiceServer {
  public static void main(String[] args) {
  SpringApplication.run(InvoiceServer.class, args);
  }
}

Spring Cloud Netflix - Hystrix

Les systèmes sont malheureusement tous imprévisible. Peu importe l’effort investi sur à rentre le système réslient, il y aura toujours un cas où une dépendance ne sera pas joignable. Hystrix permet de prendre le controle de manière élégante sur ces dépendances et nous donnent l’opportunité de prévoir un traitement de secours lorsque qu’une dépendance ne répond plus.

Spring Cloud Netflix - Hystrix

images/solutions/hystrix-1.png

Spring Cloud Netflix - Hystrix

images/solutions/hystrix-2.png

Intégrer Hystrix dans un projet Spring Cloud

pom.xml
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
  </dependency>

Déclaration d’un fallback

Commande Hystrix
@HystrixCommand(fallbackMethod = "fallback")
public List<String> method() {
  return dependency.method();
}

public List<String> fallback( {
  return Arrays.asList();
}

Atelier 10 - Test de fallback

Objectif:

Spring Cloud Netflix - Zuul

Zuul est un Reverse Proxy HTTP architecture. Il est utilisé par Netflix pour les besoins suivant :

Zuul supporte un système de filtre pouvant être écrit en Java ou en Groovy.

Zuul avec Spring Cloud

L’utilisation principale au sein de Spring Cloud pour Zuul se résume à un role de reverse proxy pour abstraire la complexité d’une architecture aux clients externe. L’intégration avec le Service Discovery de Spring Cloud et l’utilisation de Ribbon rend ce travail de redirection presque transparant.

Par défaut, Zuul expoera tous les services Eureka à travers l’url /servierId/**. Une fois la requête passé le proxy, la première partie de son URL sera supprimé afin que le reste de la requête soit redirigé au service cible.

Un projet avec Zuul

pom.Xml
Note Le retry automatique de Zuul après une première erreur se doit d'être configuré manuellement avec la configuration zuul.routes.route-name.retryable=true. Autrement Zuul retournera des erreurs lorsque l’un des microservice qu’il tente d’appeller est indisponible.

Spring Cloud Sleuth

Sleuth vise à répondre à la problématique des services de tracage distribué imposé par les architecture microservice. De manière transparante,

Sleuth consiste en une implémentation de l’essaie Dapper rédigé par Google

Note
Etude sur le tracage distribué par google
http://research.google.com/pubs/pub36356.html

Récupérer des logs Sleuth avec Logstash

Pour une sortie console
filter {
  # pattern matching logback pattern
  grok {
  match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
  }
}
Pour une sortie Cloud Foundry
filter {
  # pattern matching logback pattern
  grok {
  match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
  }
}

Spring Cloud Stream

Spring Cloud Stream se veut comme une boite à outil pour simplifier la messagerie entre application de type microservice. Les services en fesant usage auront besoin de se reposer sur un broker externe.

Le projet vise à standardiser la déclaration et la configuration des queues de production et de consommation.

images/solutions/spring-cloud-stream-1.png

Un exemple de messagerie déclarative avec Stream

CloudStreamApplication.java
@SpringBootApplication
public class StreamApplication {

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

@EnableBinding(Sink.class)
public class TimerSource {

  ...

  @StreamListener(Sink.INPUT)
  public void processVote(Vote vote) {
    votingService.recordVote(vote);
  }
}

Spring Cloud Stream

Spring Cloud Security

Spring Cloud préconise l’utilisation de OAuth pour l’authentification à travers votre plateforme. Par conséquent la projet Spring Cloud Security offre les fonctionnalités suivantes :

Spring Cloud Bus

Module plutot expériental qui permet aux application Spring Cloud de mettre à jour leur configuration Spring dynamiquement. Ce système se base sur Kafka ou AMQP. Dans l’esprit, le bus sert à communiquer des messages à tous les noeuds qui répondent au même service Eureka.