Par exemple, vous construisez une plateforme de commerce électronique avec des microservices pour la gestion des stocks, les paiements et les expéditions. Un client passe une commande, et soudain vous êtes confronté à la question éternelle : comment garantir que tous ces services fonctionnent bien ensemble sans vous faire perdre la tête ?
Les transactions ACID traditionnelles sont excellentes pour les monolithes, mais elles ne conviennent pas au monde des microservices. C'est comme essayer d'utiliser un marteau-pilon pour casser une noix - excessif et susceptible de causer plus de problèmes qu'il n'en résout. C'est là que LRA intervient pour sauver la situation.
Pourquoi ACID ne fonctionne pas :
- Couplage étroit entre les services (un grand non-non dans les microservices)
- Goulots d'étranglement de performance dus au verrouillage
- Problèmes de scalabilité à mesure que votre système grandit
LRA adopte une approche différente. Au lieu de forcer des transactions atomiques entre les services, il adopte le modèle de cohérence éventuelle. C'est comme coordonner une routine de danse où chaque danseur (service) connaît son rôle et sait comment se rattraper si quelqu'un lui marche sur les pieds.
MicroProfile LRA
Alors, qu'est-ce que MicroProfile LRA exactement ? Pensez-y comme à un chorégraphe pour votre ballet de microservices. Il fournit un moyen standardisé de gérer des opérations longues et distribuées qui s'étendent sur plusieurs services.
Concepts Clés :
- Actions Longues : Opérations qui peuvent prendre des secondes, des minutes, voire des heures pour se terminer
- Compensation : La capacité d'annuler des actions si les choses tournent mal
- Cohérence Éventuelle : Accepter le fait que la cohérence entre les services prend du temps
LRA ne cherche pas à imposer une cohérence immédiate. Au lieu de cela, il vous donne des outils pour gérer le cycle de vie de ces opérations longues et garantir qu'à terme, votre système atteigne un état cohérent.
Mise en Place de LRA : Un Guide Étape par Étape
Prêt à ajouter un peu de magie LRA à votre projet ? Voyons comment le mettre en place :
1. Ajouter la dépendance MicroProfile LRA
Tout d'abord, vous devrez ajouter la dépendance MicroProfile LRA à votre projet. Si vous utilisez Maven, ajoutez ceci à votre pom.xml :
<dependency>
<groupId>org.eclipse.microprofile.lra</groupId>
<artifactId>microprofile-lra-api</artifactId>
<version>1.0</version>
</dependency>
2. Configurer votre coordinateur LRA
Vous aurez besoin d'un coordinateur LRA pour gérer vos LRA. Cela pourrait être un service séparé ou faire partie de votre infrastructure existante. Voici une configuration simple utilisant Quarkus :
quarkus.lra.coordinator.url=http://localhost:8080/lra-coordinator
3. Annoter vos méthodes
Maintenant vient la partie amusante - annoter vos méthodes pour participer aux LRA. Voici un exemple simple :
@Path("/order")
public class OrderService {
@POST
@Path("/create")
@LRA(LRA.Type.REQUIRED)
public Response createOrder(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) String lraId) {
// Votre logique de création de commande ici
return Response.ok().build();
}
@PUT
@Path("/complete")
@Complete
public Response completeOrder(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) String lraId) {
// Logique pour compléter la commande
return Response.ok().build();
}
@PUT
@Path("/compensate")
@Compensate
public Response compensateOrder(@HeaderParam(LRA.LRA_HTTP_CONTEXT_HEADER) String lraId) {
// Logique pour compenser (annuler) la commande
return Response.ok().build();
}
}
Dans cet exemple, createOrder
démarre ou rejoint une LRA, completeOrder
est appelé lorsque la LRA se termine avec succès, et compensateOrder
est appelé si la LRA doit être annulée.
Annotations LRA : Vos Nouveaux Meilleurs Amis
LRA est livré avec un ensemble d'annotations qui facilitent la gestion des actions longues. Décomposons les plus importantes :
@LRA
C'est la star du spectacle. Utilisez-le pour définir la portée de votre LRA :
@LRA(LRA.Type.REQUIRED)
public Response doSomething() {
// Cette méthode s'exécutera toujours dans une LRA
}
Le paramètre Type
peut être :
REQUIRED
: Rejoindre une LRA existante ou en créer une nouvelleREQUIRES_NEW
: Toujours créer une nouvelle LRAMANDATORY
: Doit être appelé dans une LRA existanteSUPPORTS
: Utiliser une LRA si présente, sinon s'exécuter sansNOT_SUPPORTED
: S'exécuter sans LRA, même si une existe
@Compensate
C'est votre filet de sécurité. Utilisez-le pour définir ce qui doit se passer si les choses tournent mal :
@Compensate
public Response undoStuff(URI lraId) {
// Logique de nettoyage ici
return Response.ok().build();
}
@Complete
Le chemin heureux. Cette méthode est appelée lorsque votre LRA se termine avec succès :
@Complete
public Response finalizeStuff(URI lraId) {
// Logique de finalisation ici
return Response.ok().build();
}
Coordonner les Actions : La Danse LRA
Maintenant que nous avons nos annotations en place, voyons comment LRA coordonne les actions entre les services. Imaginez que nous construisons une librairie en ligne avec des services séparés pour les stocks, les paiements et les expéditions.
Le Flux de Commande :
@Path("/order")
public class OrderService {
@Inject
InventoryService inventoryService;
@Inject
PaymentService paymentService;
@Inject
ShippingService shippingService;
@POST
@Path("/place")
@LRA(LRA.Type.REQUIRED)
public Response placeOrder(Order order) {
// Étape 1 : Réserver les stocks
inventoryService.reserveBooks(order.getBooks());
// Étape 2 : Traiter le paiement
paymentService.processPayment(order.getTotal());
// Étape 3 : Organiser l'expédition
shippingService.arrangeShipment(order.getShippingDetails());
return Response.ok().build();
}
@Compensate
public Response compensateOrder(URI lraId) {
// Si quelque chose tourne mal, cela sera appelé pour tout annuler
inventoryService.releaseReservation(lraId);
paymentService.refundPayment(lraId);
shippingService.cancelShipment(lraId);
return Response.ok().build();
}
@Complete
public Response completeOrder(URI lraId) {
// Cela est appelé lorsque tout réussit
inventoryService.confirmReservation(lraId);
paymentService.confirmPayment(lraId);
shippingService.confirmShipment(lraId);
return Response.ok().build();
}
}
Dans cet exemple, si une étape échoue (par exemple, le paiement ne passe pas), la méthode @Compensate
sera appelée, annulant toutes les étapes précédentes. Si tout réussit, la méthode @Complete
finalise la commande.
Délais et Politiques d'Annulation : Garder Vos LRA sous Contrôle
Les LRA sont par nature longues, mais parfois "longues" se transforme en "interminables". Pour éviter que vos LRA ne deviennent incontrôlables, MicroProfile LRA fournit des mécanismes pour les délais et l'annulation.
Définition des Délais
Vous pouvez définir un délai pour votre LRA en utilisant le paramètre timeLimit
de l'annotation @LRA
:
@LRA(value = LRA.Type.REQUIRED, timeLimit = 10, timeUnit = ChronoUnit.MINUTES)
public Response longRunningOperation() {
// Cette LRA sera automatiquement annulée après 10 minutes
// ...
}
Si la LRA ne se termine pas dans le temps imparti, elle sera automatiquement annulée, et la méthode @Compensate
sera appelée.
Annulation Manuelle
Parfois, vous pourriez avoir besoin d'annuler une LRA manuellement. Vous pouvez le faire en injectant le LRAClient
et en appelant sa méthode cancel
:
@Inject
LRAClient lraClient;
public void cancelOperation(URI lraId) {
lraClient.cancel(lraId);
}
LRA vs. Sagas : Choisir Votre Arme
À ce stade, vous pourriez penser, "Attendez une minute, cela ressemble beaucoup au modèle Saga !" Vous n'avez pas tort. Les LRA et les Sagas sont comme des cousins dans la famille des transactions distribuées. Décomposons les similitudes et les différences :
Similarités :
- Les deux gèrent des transactions longues et distribuées
- Les deux utilisent la compensation pour annuler un travail partiel
- Les deux visent la cohérence éventuelle
Différences :
- LRA est une spécification standardisée, tandis que Saga est un modèle
- LRA offre un support intégré via des annotations, ce qui le rend plus facile à implémenter
- Les Sagas utilisent généralement des événements pour la coordination, tandis que LRA utilise des en-têtes HTTP
Alors, quand devriez-vous utiliser LRA plutôt que Sagas ?
- Si vous utilisez un framework compatible MicroProfile (comme Quarkus ou Open Liberty)
- Lorsque vous souhaitez une approche standardisée avec moins de code passe-partout
- Si vous préférez un style plus déclaratif utilisant des annotations
À l'inverse, les Sagas pourraient être un meilleur choix si :
- Vous avez besoin d'un contrôle plus fin sur le processus de compensation
- Votre système est fortement axé sur les événements
- Vous n'utilisez pas un framework compatible MicroProfile
Surveillance et Gestion des Cycles de Vie LRA
Maintenant que nous avons nos LRA en cours d'exécution, comment les surveiller ? La surveillance des LRA est cruciale pour comprendre la santé et la performance de vos transactions distribuées.
Métriques à Surveiller
MicroProfile LRA fournit plusieurs métriques prêtes à l'emploi :
lra_started
: Nombre de LRA démarréeslra_completed
: Nombre de LRA terminées avec succèslra_cancelled
: Nombre de LRA annuléeslra_duration
: Durée des LRA
Vous pouvez exposer ces métriques en utilisant MicroProfile Metrics. Voici comment vous pourriez le configurer dans Quarkus :
quarkus.smallrye-metrics.extensions.lra.enabled=true
Intégration avec les Outils de Surveillance
Une fois que vous avez exposé vos métriques, vous pouvez les intégrer avec des outils de surveillance populaires. Voici un exemple rapide de configuration de scrape Prometheus :
scrape_configs:
- job_name: 'lra-metrics'
metrics_path: '/metrics'
static_configs:
- targets: ['localhost:8080']
Et une simple requête de tableau de bord Grafana pour visualiser les durées des LRA :
rate(lra_duration_seconds_sum[5m]) / rate(lra_duration_seconds_count[5m])
Journalisation et Alertes
N'oubliez pas de configurer une journalisation appropriée pour vos LRA. Voici un exemple simple utilisant SLF4J :
@Inject
Logger logger;
@LRA
public void someOperation() {
logger.info("Starting LRA: {}", Context.getObjectId());
// ...
}
@Complete
public void completeOperation() {
logger.info("Completing LRA: {}", Context.getObjectId());
// ...
}
@Compensate
public void compensateOperation() {
logger.warn("Compensating LRA: {}", Context.getObjectId());
// ...
}
Configurez des alertes pour les LRA annulées ou les LRA qui dépassent certaines durées pour détecter les problèmes potentiels tôt.
Exemples Réels : LRA en Action
Voyons quelques scénarios réels où MicroProfile LRA brille :
Exemple 1 : Système de Réservation de Voyage
Imaginez un système de réservation de voyage où les utilisateurs peuvent réserver des vols, des hôtels et des locations de voitures en une seule transaction. Voici comment vous pourriez le structurer en utilisant LRA :
@Path("/booking")
public class BookingService {
@Inject
FlightService flightService;
@Inject
HotelService hotelService;
@Inject
CarRentalService carRentalService;
@POST
@Path("/create")
@LRA(LRA.Type.REQUIRES_NEW)
public Response createBooking(BookingRequest request) {
flightService.bookFlight(request.getFlightDetails());
hotelService.reserveRoom(request.getHotelDetails());
carRentalService.rentCar(request.getCarDetails());
return Response.ok().build();
}
@Compensate
public Response compensateBooking(URI lraId) {
flightService.cancelFlight(lraId);
hotelService.cancelReservation(lraId);
carRentalService.cancelRental(lraId);
return Response.ok().build();
}
@Complete
public Response completeBooking(URI lraId) {
flightService.confirmFlight(lraId);
hotelService.confirmReservation(lraId);
carRentalService.confirmRental(lraId);
return Response.ok().build();
}
}
Dans cet exemple, si une partie de la réservation échoue (par exemple, l'hôtel est complet), toute la transaction est annulée, garantissant que l'utilisateur ne se retrouve pas avec des réservations partielles.
Exemple 2 : Traitement des Commandes E-commerce
Voici comment un système de traitement des commandes e-commerce pourrait utiliser LRA pour gérer les complexités de l'exécution des commandes :
@Path("/order")
public class OrderProcessingService {
@Inject
InventoryService inventoryService;
@Inject
PaymentService paymentService;
@Inject
ShippingService shippingService;
@Inject
NotificationService notificationService;
@POST
@Path("/process")
@LRA(LRA.Type.REQUIRES_NEW, timeLimit = 30, timeUnit = ChronoUnit.MINUTES)
public Response processOrder(Order order) {
String orderId = order.getId();
inventoryService.reserveItems(orderId, order.getItems());
paymentService.processPayment(orderId, order.getTotalAmount());
String trackingNumber = shippingService.createShipment(orderId, order.getShippingAddress());
notificationService.sendOrderConfirmation(orderId, trackingNumber);
return Response.ok().build();
}
@Compensate
public Response compensateOrder(URI lraId) {
String orderId = extractOrderId(lraId);
inventoryService.releaseReservedItems(orderId);
paymentService.refundPayment(orderId);
shippingService.cancelShipment(orderId);
notificationService.sendOrderCancellationNotice(orderId);
return Response.ok().build();
}
@Complete
public Response completeOrder(URI lraId) {
String orderId = extractOrderId(lraId);
inventoryService.confirmItemsShipped(orderId);
paymentService.finalizePayment(orderId);
shippingService.dispatchShipment(orderId);
notificationService.sendShipmentDispatchedNotification(orderId);
return Response.ok().build();
}
private String extractOrderId(URI lraId) {
// Extraire l'ID de commande à partir de l'ID LRA
// ...
}
}
Cet exemple montre comment LRA peut gérer un flux de traitement de commande complexe, garantissant que toutes les étapes (gestion des stocks, traitement des paiements, expédition et notification des clients) sont coordonnées et peuvent être annulées si nécessaire.
Conclusion : Adopter l'Harmonie Distribuée avec LRA
MicroProfile LRA apporte un souffle d'air frais au monde des transactions distribuées. Il fournit une approche standardisée, basée sur des annotations, pour gérer des actions longues entre microservices, trouvant un équilibre entre cohérence et réalités des systèmes distribués.
Points clés à retenir :
- LRA adopte la cohérence éventuelle, ce qui en fait un excellent choix pour les architectures de microservices
- L'approche basée sur les annotations réduit le code passe-partout et facilite la compréhension des limites des transactions
- Le support intégré pour la compensation permet de gérer gracieusement les échecs
- L'intégration avec MicroProfile facilite la surveillance et les métriques
Alors que vous vous aventurez dans le monde des transactions distribuées, envisagez de donner une chance à MicroProfile LRA. Cela pourrait bien être l'ingrédient secret que vos microservices attendaient !
"Dans les systèmes distribués, le parfait est l'ennemi du bien. LRA nous aide à atteindre une cohérence 'suffisamment bonne' sans sacrifier la scalabilité ou la performance."
Rappelez-vous, bien que LRA soit puissant, ce n'est pas une solution miracle. Considérez toujours votre cas d'utilisation spécifique et vos exigences lorsque vous choisissez entre LRA, Sagas ou d'autres modèles de transactions distribuées.
Bon codage, et que vos transactions distribuées soient toujours en votre faveur !