Dans les microservices complexes, la gestion avancée des erreurs va au-delà de la simple journalisation des exceptions. Une gestion efficace des erreurs est cruciale pour la fiabilité, l’évolutivité et le maintien d’une bonne expérience utilisateur. Cet article couvrira les techniques avancées de gestion des erreurs dans les microservices Spring Boot, en se concentrant sur les stratégies de gestion des erreurs dans les systèmes distribués, la gestion des tentatives, la création de réponses d'erreur personnalisées et la journalisation des erreurs de manière à faciliter le débogage.
Commençons par une approche fondamentale de gestion des erreurs dans Spring Boot pour établir une référence.
Spring Boot fournit un gestionnaire d'exceptions global avec @ControllerAdvice et @ExceptionHandler. Cette configuration nous permet de gérer les exceptions sur tous les contrôleurs en un seul endroit.
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) { ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage()); return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex) { ErrorResponse error = new ErrorResponse("INTERNAL_SERVER_ERROR", "An unexpected error occurred."); return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); } }
Ici, ErrorResponse est un modèle d'erreur personnalisé :
public class ErrorResponse { private String code; private String message; // Constructors, Getters, and Setters }
S'assurer que toutes les exceptions renvoient un format de réponse d'erreur cohérent (par exemple, ErrorResponse) aide les clients à interpréter correctement les erreurs.
L'attribution d'un ID d'erreur unique à chaque exception permet de suivre les erreurs spécifiques entre les services. Cet identifiant peut également être enregistré avec les détails de l'exception pour un débogage plus facile.
@ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex) { String errorId = UUID.randomUUID().toString(); log.error("Error ID: {}, Message: {}", errorId, ex.getMessage(), ex); ErrorResponse error = new ErrorResponse("INTERNAL_SERVER_ERROR", "An unexpected error occurred. Reference ID: " + errorId); return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); }
Les clients reçoivent une réponse d'erreur contenant l'ID d'erreur, qu'ils peuvent signaler au support, en les reliant directement aux journaux détaillés.
Dans les systèmes distribués, les problèmes transitoires (comme les délais d'attente du réseau) peuvent être résolus par une nouvelle tentative. Utilisez @Retryable de Spring pour la logique de nouvelle tentative sur les méthodes de service.
Tout d'abord, ajoutez la dépendance Spring Retry dans votre pom.xml :
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
Ensuite, activez Spring Retry avec @EnableRetry et annotez les méthodes qui nécessitent des nouvelles tentatives.
@EnableRetry @Service public class ExternalService { @Retryable( value = { ResourceAccessException.class }, maxAttempts = 3, backoff = @Backoff(delay = 2000)) public String callExternalService() throws ResourceAccessException { // Code that calls an external service } @Recover public String recover(ResourceAccessException e) { log.error("External service call failed after retries.", e); return "Fallback response due to error."; } }
Cette configuration retente la méthode jusqu'à 3 fois, avec un délai de 2 secondes entre les tentatives. Si toutes les tentatives échouent, la méthode de récupération s'exécute comme solution de secours.
Pour la gestion des erreurs dans les appels de service à service, Feign fournit un moyen déclaratif de configurer les tentatives et les solutions de secours.
Définissez un client Feign avec un support de secours :
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) { ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage()); return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex) { ErrorResponse error = new ErrorResponse("INTERNAL_SERVER_ERROR", "An unexpected error occurred."); return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); } }
Cette approche garantit que si le service d'inventaire n'est pas disponible, InventoryServiceFallback intervient avec une réponse prédéfinie.
Configurez une pile ELK (Elasticsearch, Logstash, Kibana) pour consolider les journaux de plusieurs microservices. Avec un système de journalisation centralisé, vous pouvez facilement retracer les problèmes entre les services et afficher les journaux avec les ID d'erreur associés.
Par exemple, configurez les modèles de journaux dans application.yml :
public class ErrorResponse { private String code; private String message; // Constructors, Getters, and Setters }
Dans les systèmes distribués, le traçage d'une seule transaction sur plusieurs services est essentiel. Spring Cloud Sleuth fournit un traçage distribué avec des identifiants de trace et d'étendue uniques.
Ajoutez Spring Cloud Sleuth dans vos dépendances :
@ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleGeneralException(Exception ex) { String errorId = UUID.randomUUID().toString(); log.error("Error ID: {}, Message: {}", errorId, ex.getMessage(), ex); ErrorResponse error = new ErrorResponse("INTERNAL_SERVER_ERROR", "An unexpected error occurred. Reference ID: " + errorId); return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); }
Définissez des exceptions personnalisées pour fournir une gestion des erreurs plus spécifique.
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
Personnalisez les réponses d'erreur en implémentant ErrorAttributes pour des messages d'erreur structurés et enrichis.
@EnableRetry @Service public class ExternalService { @Retryable( value = { ResourceAccessException.class }, maxAttempts = 3, backoff = @Backoff(delay = 2000)) public String callExternalService() throws ResourceAccessException { // Code that calls an external service } @Recover public String recover(ResourceAccessException e) { log.error("External service call failed after retries.", e); return "Fallback response due to error."; } }
Enregistrez CustomErrorAttributes dans votre configuration pour personnaliser automatiquement toutes les réponses d'erreur.
Utilisez le format Détails du problème pour une structure d'erreur d'API standardisée. Définir un modèle de réponse aux erreurs basé sur la RFC 7807 :
@FeignClient(name = "inventory-service", fallback = InventoryServiceFallback.class) public interface InventoryServiceClient { @GetMapping("/api/inventory/{id}") InventoryResponse getInventory(@PathVariable("id") Long id); } @Component public class InventoryServiceFallback implements InventoryServiceClient { @Override public InventoryResponse getInventory(Long id) { // Fallback logic, like returning cached data or an error response return new InventoryResponse(id, "N/A", "Fallback inventory"); } }
Ensuite, renvoyez cette réponse structurée à partir des méthodes @ControllerAdvice pour maintenir une structure d'erreur cohérente dans toutes les API.
logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
L'intégration d'un modèle de disjoncteur protège votre microservice contre les appels répétés d'un service défaillant.
Utilisation du disjoncteur Resilience4j
Ajoutez Resilience4j à vos dépendances :
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency>
Ensuite, enveloppez une méthode avec un disjoncteur :
public class InvalidRequestException extends RuntimeException { public InvalidRequestException(String message) { super(message); } }
Cette configuration arrête d'appeler getInventory si elle échoue plusieurs fois, et inventorierFallback renvoie une réponse sûre à la place.
La gestion avancée des erreurs dans les microservices Spring Boot comprend :
Gestion centralisée des erreurs pour des réponses cohérentes et un débogage simplifié.
Réessais et disjoncteurs pour des appels de service à service résilients.
Journalisation et traçabilité centralisées avec des outils comme ELK et Sleuth.
Formats d'erreur personnalisés avec détails du problème et réponses d'erreur structurées.
Ces techniques contribuent à garantir la robustesse de vos microservices, en fournissant des réponses aux erreurs cohérentes et traçables tout en évitant les pannes en cascade entre les services.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!