Erreurs de gestion des mauvaises pratiques
Cet article fait suite à mon précédent article sur la gestion des erreurs en 3 étapes.
Dans cet article, je souhaite montrer quelques mauvaises pratiques en matière de gestion des erreurs et peut-être quelques solutions rapides, si vous trébuchez
à travers eux dans votre base de code.
Avertissement, cet article de blog n'est qu'une opinion personnelle et vous devrez ajuster les recommandations à votre cas d'utilisation spécifique.
1. Avaler des exceptions
Je tombe régulièrement sur des bases de code où les exceptions sont silencieusement ignorées. Peut-être que le développeur était pressé ou voulait gérer les cas d'erreur plus tard ? Quoi qu'il en soit, c'est une mauvaise pratique car si l'erreur se produit :
- l'utilisateur ne recevra aucun retour et pourra penser que l'opération a réussi.
- il n'y a pas de trace de pile ou de message d'erreur à enquêter.
- Parfois, il n'y a même pas d'entrée de journal appropriée, ce qui rend presque impossible la détection de l'erreur.
Mauvais exemple :
/** * Reads some data from another service or repo and returns it. * The repo might throw an exception, but we don't care. */ public Mono<String> getHello() { try { return helloRepository.getHello() .map("Hello %s"::formatted); } catch (Exception e) { // do nothing return Mono.just("Hello World"); } }
Dans cet exemple, nous pourrions nous demander pourquoi certains utilisateurs se plaignent de ne pas recevoir le bon message de bonjour, mais nous ne voyons aucune erreur, nous pourrions penser que les utilisateurs sont simplement confus.
Option 1 - Conserver le try-catch et gérer l'erreur
Gérez l'erreur là où elle se produit, par ex. déjà dans le référentiel si possible.
Si vous devez le gérer ici, enregistrez au moins l'erreur et renvoyez un message d'erreur approprié.
Refactoring minimal
- enregistrer l'erreur (éventuellement également le stacktrace)
- renvoie un message d'erreur approprié
public Mono<String> getHello() { try { return helloRepository.getHello() .map("Hello %s"::formatted); } catch (Exception e) { log.error("Error reading hello data from repository - {}", e.getMessage(), e); return Mono.error(new RuntimeException("Internal error reading hello data %s".formatted(e.getMessage()), e)); } }
REMARQUE :
Cela pourrait casser le code en amont puisque vous renvoyez maintenant une erreur au lieu d'une valeur par défaut.
Option 2 – Refactorisation appropriée du flux réactif
Il serait préférable de gérer l'erreur dans l'API du flux réactif
public Mono<String> getHelloHandling() { return helloRepository.getHello() .map("Hello %s"::formatted) // wrap the exception in a RuntimeException with a meaningful message .onErrorMap(e -> new RuntimeException("Internal error reading hello data from HelloRepository` %s". formatted(e.getMessage()), e)); }
1b. Avaler des exceptions - avec un flux réactif
Le modèle de l'exemple 1 apparaît également dans les flux réactifs :
public Mono<String> getHello() { return helloRepository.getHello() .map("Hello %s"::formatted) // do nothing on error, just return a default value .onErrorReturn("Hello world"); }
Ça a l'air très joli et propre, non ? Mais nous ne pourrons pas détecter que l'erreur est levée dans le référentiel !
S'il existe une valeur par défaut, au moins un journal des erreurs doit être rédigé.
Atténuation
Comme dans l'exemple précédent, nous encapsulons l'exception dans une autre exception, cette fois dans une exception personnalisée qui facilite même la détection de l'endroit spécifique où cette exception est levée
public Mono<String> getHello2() { return helloRepository.getHello() .map("Hello %s"::formatted) .onErrorMap( e -> new CustomException("Error reading hello-data from repository - %s".formatted(e.getMessage()), e)); }
2. Enregistrer à plusieurs reprises le stacktrace pour la même exception
L'opposé de la suppression silencieuse des exceptions consiste à enregistrer la même exception plusieurs fois. C'est une mauvaise pratique car elle confond les journaux avec des centaines ou des milliers de lignes de stacktraces sans apporter de signification supplémentaire.
Dans mon pire exemple, j'ai trouvé la même trace de pile cinq fois dans les journaux sans aucun message significatif.
Mauvais exemple :
Le contrôleur :
@RestController @AllArgsConstructor @Slf4j public class HelloController { private final HelloService helloService; @GetMapping("/hello") public Mono<ResponseEntity<String>> hello() { return helloService.getHello() .map(ResponseEntity::ok) .defaultIfEmpty(ResponseEntity.notFound().build()) .onErrorResume(e -> { log.error("Error:", e); return Mono.error(e); }); } }
Et dans le Service :
@Service @AllArgsConstructor @Slf4j public class HelloService { private final HelloRepository helloRepository; /** * Reads some data from another service or repo and returns it. */ public Mono<String> getHello() { return helloRepository.getHello() .map("Hello %s"::formatted) .onErrorResume(e -> { log.error("Error:", e); return Mono.error(e); }); } }
... et probablement dans d'autres endroits...
Atténuation
Ceci est expliqué dans mon article précédent Gestion des erreurs en 3 étapes, je ne montrerai donc pas le code ici, mais plutôt une recommandation :
- Avoir un gestionnaire d'erreurs global qui enregistre l'erreur et renvoie un message d'erreur approprié à l'utilisateur.
- Dans le code, évitez le stacktrace mais
- enveloppez l'exception dans une exception personnalisée ou une exception d'exécution avec un message significatif
- enregistrer le message d'erreur (pas la trace complète de la pile)
- enregistrer la trace de pile uniquement dans le gestionnaire d'erreurs global
3. Détectez les exceptions génériques
La détection d'exceptions génériques telles que Exception ou Throwable peut entraîner un comportement involontaire et rendre le débogage assez difficile. Il est préférable d'attraper des exceptions spécifiques.
Mauvais exemple
public Mono<String> getHello() { try { return helloRepository.getHello(); } catch (Exception e) { log.error("Error while fetching hello data", e); return Mono.empty(); } }
Atténuation
Détectez des exceptions spécifiques pour gérer différents scénarios d'erreur de manière appropriée.
public Mono<String> getHello() { try { return helloRepository.getHello(); } catch (SQLException e) { return Mono.error(new HelloDataException("Database error while getting hello-data - %s".formatted(e.getMessage()), e)); } catch (IOException e) { // maybe perform a retry? return Mono.error(new HelloDataException("IO error while getting hello-data - %s".formatted(e.getMessage()), e)); } }
et l'équivalent en utilisant l'API de flux réactif
public Mono<String> getHello() { return helloRepository.getHello() .onErrorMap(SQLException.class, e -> new HelloDataException("Database error while getting hello-data - %s".formatted(e.getMessage()), e)) .onErrorMap(IOException.class, e -> new HelloDataException("IO error while getting hello-data - %s".formatted(e.getMessage()), e)); }
4. (Sur)utilisation des exceptions vérifiées
À mon avis, les exceptions vérifiées sont une erreur en Java, elles ne sont pas très utiles et conduisent souvent à de mauvaises pratiques et à un code encombré.
Mauvais exemple
public void someMethod() throws IOException, SQLException { // some code that might throw an exception }
Avec les exceptions vérifiées, vous devez les gérer dans le code appelant, ce qui rend plus difficile l'utilisation d'autres modèles, par ex. programmation fonctionnelle ou flux réactifs ou au printemps le gestionnaire d'erreurs global.
Exception :
Les exceptions cochées sont utiles, par ex. dans le code de la bibliothèque, où vous souhaitez forcer l'utilisateur à gérer l'exception.
Atténuation
Utilisez des exceptions non contrôlées pour les scénarios dans lesquels on ne peut raisonnablement pas s'attendre à ce que l'appelant récupère.
public void someMethod() { try { // some code that might throw an exception } catch (IOException | SQLException e) { throw new RuntimeException("An error occurred - %s".formatted(e.getMessage()), e); } }
Using Exceptions for Control Flow
Using exceptions for control flow makes the code hard to understand and can lead to performance issues.
Bad Example
try { int value = Integer.parseInt("abc"); } catch (NumberFormatException e) { // handle the case where the string is not a number }
Mitigation
Use a regular flow control mechanism like an if-statement.
String value = "abc"; if (value.matches("\\d+")) { int number = Integer.parseInt(value); } else { // handle the case where the string is not a number }
Conclusion
In this article I showed some bad practices in error handling and how to mitigate them.
I hope you found this article helpful and maybe you can use some of the recommendations in your codebase and in your next refactoring.
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!

Outils d'IA chauds

Undress AI Tool
Images de déshabillage gratuites

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Les fils virtuels présentent des avantages de performances significatifs dans les scénarios très concurrencés et IO, mais l'attention doit être accordée aux méthodes de test et aux scénarios applicables. 1. Les tests corrects devraient simuler des affaires réelles, en particulier les scénarios de blocage IO, et utiliser des outils tels que JMH ou Gatling pour comparer les threads de plate-forme; 2. L'écart de débit est évident, et il peut être plusieurs fois à dix fois supérieur à 100 000 demandes simultanées, car elle est plus légère et efficace dans la planification; 3. Pendant le test, il est nécessaire d'éviter de poursuivre aveuglément des nombres de concurrence élevés, de s'adapter aux modèles IO non bloquants et de prêter attention aux indicateurs de surveillance tels que la latence et le GC; 4. Dans les applications réelles, elle convient au backend Web, au traitement des tâches asynchrones et à un grand nombre de scénarios IO simultanés, tandis que les tâches à forte intensité de processeur sont toujours adaptées aux threads de plate-forme ou à Forkjoinpool.

ServiceMesh est un choix inévitable pour l'évolution de l'architecture de microservice Java, et son cœur réside dans le découplage de la logique réseau et du code commercial. 1. ServiceMesh gère l'équilibrage de la charge, le fusible, la surveillance et d'autres fonctions par le biais d'agents side-car pour se concentrer sur les entreprises; 2. Istio Envoy convient aux projets moyens et grands, et Linkerd est plus léger et adapté aux essais à petite échelle; 3. Les microservices Java devraient fermer la feigne, le ruban et d'autres composants et les remettre à Istiod pour la découverte et la communication; 4. Assurer l'injection automatique de side-car pendant le déploiement, prêter attention à la configuration des règles de trafic, à la compatibilité du protocole et à la construction du système de suivi des journaux, et adoptez la planification incrémentielle de la migration et de la surveillance pré-contrôler.

Pour gérer correctement les transactions JDBC, vous devez d'abord désactiver le mode de validation automatique, puis effectuer plusieurs opérations, et enfin vous engager ou randonner en fonction des résultats; 1. Appelez Conn.SetAutoCommit (false) pour démarrer la transaction; 2. Exécuter plusieurs opérations SQL, telles que l'insertion et la mise à jour; 3. Appelez Conn.Commit () Si toutes les opérations sont réussies, et appelez Conn.Rollback () Si une exception se produit pour garantir la cohérence des données; Dans le même temps, les ressources TRY-With doivent être utilisées pour gérer les ressources, gérer correctement les exceptions et clôturer les connexions pour éviter la fuite de connexion; De plus, il est recommandé d'utiliser des pools de connexion et de définir des points de sauvegarde pour réaliser un retour en arrière partiel, et de maintenir les transactions aussi courtes que possible pour améliorer les performances.

DépendanceInjection (DI) IsadesignPatternwhereBjectSeveveveltency dexternal, promotionnloosecouplingAndreasiestingthroughroughConstructor, seter, orfieldInjection.2.springframeworkusesannotations like @ composant, @ service et @ autowiredwithjava-baskusecondotations like @ composant, @ service et @ autowiredwithjava-basesConfitations lik

Pré-formancetartuptimemoryusage, quarkusandmicronautleadduetocompile-timeprocessingandgraalvsupport, withquarkusofperforming lightbetterine scénarios.

Setupamaven / gradleprojectwithjax-rsDependces likejersey; 2.CreateArestResourceUsingannotationsSuchas @ pathand @ get; 3.ConfigureTheApplicationViaApplicationsUbclassorweb.xml; 4.AddjacksonforjsonBindingByCludingJersey-Media-Json-Jackson; 5.DeploEp

Utilisez des classes dans le package Java.Time pour remplacer les anciennes classes de date et de calendrier; 2. Obtenez la date et l'heure actuelles via LocalDate, LocalDateTime et Localtime; 3. Créez une date et une heure spécifiques en utilisant la méthode OF (); 4. Utilisez la méthode plus / moins pour augmenter et diminuer le temps; 5. Utilisez ZonedDateTime et ZoneID pour traiter le fuseau horaire; 6. Format et chaînes de date d'analyse via DateTimeFormatter; 7. Utilisez instantanément pour être compatible avec les anciens types de dates si nécessaire; Le traitement des dattes dans le Java moderne devrait donner la priorité à l'utilisation de Java.timeapi, qui fournit clairement, immuable et linéaire

Utilisez des outils d'analyse des performances pour localiser les goulots d'étranglement, utiliser VisualVM ou JProfiler dans la phase de développement et de test, et donner la priorité à l'async-profil dans l'environnement de production; 2. Réduire la création d'objets, réutiliser les objets, utiliser StringBuilder pour remplacer l'épissage de la chaîne et sélectionner les stratégies GC appropriées; 3. Optimiser l'utilisation de la collection, sélectionner et prérégler la capacité initiale selon la scène; 4. Optimiser la concurrence, utiliser des collections simultanées, réduire la granularité de verrouillage et régler raisonnablement le pool de threads; 5. Assurez les paramètres JVM, définissez la taille du tas raisonnable et le collecteur de déchets à faible latence et activez les journaux GC; 6. Évitez la réflexion au niveau du code, remplacez les classes de wrapper par des types de base, retardez l'initialisation et utilisez final et statique; 7. Test et surveillance des performances continues, combinées avec JMH
