Bus de messages et Patrons d’intégration

M2TIW - TIW1 - Emmanuel Coquery

Composition de services

Un service peut en cacher d’autres (A=B+C)

Un service peut être vu comme une interface sur:

  • un / des processus métier
  • utilisant à leur tour des services
    • qui réalisent des actions à plus petite échelle / moins complexes
  • “analyse descendante” vers SOA
    • attention aux limites de l’analogie !

Pourquoi composer des services ?

Dans le cadre de gros systèmes d’information :

  • Logique applicative complexe
  • Processus décomposables
  • Décentralisation des traitements

Quelques problématiques autour de la composition

  • Liées aux processus métiers complexes

    • Transactions longues, instances multiples, concurrence, distribution
    • Point de vue: Orchestration vs chorégraphies
  • Gestion des messages

    • Comment organiser la transmission
    • Quelle efficacité ?

Transactions de longue durée

Persistance de l’état du processus

  • Stocker l’état (au sens automate) + données de chaque instance
  • Penser le process comme une gestion de ressources (type REST)

⚠️ Impact sur la conception ⚠️

Transactions métier

Attention à la répartition (commit à 2 ou 3 phases)

Pas systématiquement de rollback

  • Impacts d’une action informatique\ dans le monde réel\
    • e.g. livraison effectuée
  • Compensation au lieu du rollback
    • e.g. renvoi des produits, remboursement, etc.
  • une compensation peut être une transaction

Parallélisme et concurrence

Tâche à exécuter de manière concurrente / parallèle

  • Dépendances avec les tâches
  • Synchronisation

Gestion des dépendances entre tâches

  • Moteur de workflow
    ⚠️ Exceptions dans le flot “normal” ⚠️

  • Système de règles ECA (Event-Condition-Action)
    ⚠️ Comportement parfois complexe ⚠️

Synchronisation des tâches

les tâches dont on dépend sont terminées ?

  • une
  • toutes
  • une combinaison

Orchestration vs chorégraphies

Points de vue différents sur une composition

Complémentaires

Orchestration

  • Vue centralisée
  • Un service gère
    • le processus
    • les appels aux autres services

e.g. gestion d’un ticket dans un gestionnaire de bugs

Chorégraphie

Processus géré de manière distribuée

e.g. authentification type Kerberos,
paiement via une tierce partie

⇨ Représentation via diagramme de séquence

Instances de processus métier

Suite d’actions correspondant au traitement d’une demande particulière

e.g. déroulé de la commande n°123456

Instances et corrélations

Comment savoir si deux messages sont liés ?

  • (méta)données, e.g. identifiant de session
  • plus complexe:
    • acteurs non connus au démarrage du processus .eg. un nouveau participant arrive en cours d’exécution
    • identifiants non partagés entre acteurs
    • processus / service partagé entre plusieurs autres processus

Limites des services simples dans un cadre SOA

  • Garder l’aspect couplage faible
  • Adresses / points d’accès
    • fixes / utilisation d’un annuaire
      → complexe si beaucoup de services
  • Interactions complexes codées dans chaque client

Patrons d’intégration

Design patterns spécifiques à l’intégration dans les systèmes d’information

Basé sur la manipulation et la transmission de messages métier

≠ catégories: canaux, routage, transformation, etc

Quelques exemples de patrons d’intégration

Queues

Chaque message transmis 1 fois

Topics

≈ broadcast

Canaux persistants

Pour ne pas perdre de message

Consommateur

  • Synchrone
    Le consommateur récupère explicitement le message avec un appel bloquant

  • Asynchrone
    Le consommateur enregistre un callback

Similaire à synchrone vs asynchrone en programmation client/serveur

Frameworks de (transmission de) messages

Aller au delà de REST / SOAP+HTTP

  • Performance (binaire, protocole dédié, etc)
  • Intégration de
    • capacités de routage
    • garanties sur les messages
      (exactly once, persistance)
  • Rôle de tampon en cas de charge temporaire

Sérialisation: quelques implémentations

texte (JSON / XML + compression)

ou binaire:

Transmission: quelques standards

  • AMQP: multi-langage, fiabilité, queues, topics
  • MQTT: multi-langage, léger
  • JMS: standard Java, fiabilité, queues, topics

Transmission: quelques implémentations

  • RabbitMQ: queues, deliver exactly once, clusters, routing, AMQP, MQTT, langages variés
  • ActiveMQ: topics, queues, apache, EIP via intégration avec Apache Camel, AMQP, JMS, langages variés
  • Kafka: topics, clusters, traitement de flux, langages variés, bonne capacité d’absorption des pics de messages
  • ZeroMQ: basique, léger, efficace, langages variés

Pipeline de services

Traitement composite de messages

  • 1 étape = 1 appel à un service
  • réponse d’un service = entrée du service suivant

Filtres: messages / contenu

  • Peuvent supprimer (des parties d’) un message
  • Se combinent bien avec les pipelines

Transformations: restructuration

Traduction de schéma

Transformations: Encapsulation

e.g. transmission via SOAP

Transformations: Enrichissement

Routage basés sur les messages

  • Seul le contenu du message (header / payload) est utilisé pour décider de la destination
  • Possibilité de transformation préalable pour gérer les cas complexes

Routage dynamique

Choix de la destination selon l’état du système

e.g. équilibrage de charge

Pipeline dynamique

Similaire au pipeline de services, mais avec une séquence qui dépend du contenu

Division / Aggrégation

Diviseur et aggrégateur peuvent être utilisés séparément.

Réordonnancement de messages

Batch (attente, tri, traitement):
déclenchement sur quantité ou timeout

vs

Stream: file de priorité

Bus de services

Serveur / cluster

  • Hébergeant des services / composants légers (filtres, etc)
  • Fournissant / s’intégrant avec des frameworks de messages
  • Capacités de routage
  • Intègre de nombreux connecteurs
  • Système de gestion de services

Point Techno

Apache Camel

Apache Camel

DSL à travers une API Java
pour assembler des EIPs

CamelContext context = new DefaultCamelContext();

context.addRoutes(new RouteBuilder() {
        public void configure() {
            from("test-jms:queue:test.queue")
            .to("bean:validateOrder")
            .to("file://test");
        }
    });

Apache Camel

DSL XML
pour assembler des EIPs

<route>
   <from uri="jms:queue:order"/>
   <to uri="bean:validateOrder"/>
   <to uri="mina:tcp://mainframeip:4444?textline=true"/>
   <to uri="bean:sendConfirmEmail"/>
</route>

Apache Camel

Composants et schémas d’URL

Composants/EIP en CAMEL
=
préfixe de l’URL

⇒ 280 composants listés sur le site Web

Apache Camel

Déploiement / intégration

  • Intégrable dans de nombreux systèmes
    e.g. Spring (Boot), OSGi (Karaf), etc
  • Déployable dans des infras cloud
    e.g. Kubernetes (Camel K), Quarkus

Spring Integration

Similaire à Apache Camel

DSL XML & Java

Point Techno

RabbitMQ & Spring AMQP

RabbitMQ

Serveur de transmission de messages

Queues & Topics

Distribution (sharding)

Persistence des messages

RabbitMQ: serveur

Implémenté en Erlang (lng fonctionnel)

Facilement déployable via Docker

Interface de monitoring (management)

RabbitMQ: Clients

Nombreux langages supportés via AMQP

Intégration dans de nombreux frameworks:
e.g. Camel, Spring, etc

RabbitMQ: Clients - API

  • Connection: abstraction sur la communication réseau (socket, authentification, etc)
  • Channel: composant de gestion des messages
  • Queue: re-déclarable
  • Channel.basicPublish: envoyer un message

RabbitMQ: Clients - connect

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
     Channel channel = connection.createChannel()) {
    channel.queueDeclare(QUEUE_NAME, 
             false, false, false, null);
    // do some work
}

c.f. tutoriel

RabbitMQ: Clients - send

String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, 
         message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + message + "'");

c.f. tutoriel

RabbitMQ: Clients - receive

DeliverCallback deliverCallback = (consumerTag, delivery) -> {
    String message = new String(delivery.getBody(), "UTF-8");
    System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });

c.f. tutoriel

Avec Spring AMQP

  • RabbitTemplate pour gérer les messages
    • convertAndSend(queueName, msg) pour envoyer
  • @RabbitListener(queues) sur une classe pour recevoir
    • @RabbitHandler sur la méthode de réception

Avec Spring AMQP - send

public class Tut1Sender {

    @Autowired
    private RabbitTemplate template;

    @Scheduled(fixedDelay = 1000, initialDelay = 500)
    public void send() {
        String message = "Hello World!";
        this.template.convertAndSend("hello", message);
        System.out.println(" [x] Sent '" + message + "'");
    }
}

c.f. tutoriel

Avec Spring AMQP - receive

@RabbitListener(queues = "hello")
public class Tut1Receiver {

    @RabbitHandler
    public void receive(String in) {
        System.out.println(" [x] Received '" + in + "'");
    }
}

c.f. tutoriel

References