Dans les grandes applications monolithiques, le suivi et la surveillance des erreurs deviennent souvent inefficaces en raison d’un manque de propriété claire. Ce guide aborde le problème en proposant une approche structurée pour attribuer des responsabilités via des annotations de domaine.
Mettre en place une surveillance efficace pour un grand monolithique avec plusieurs équipes peut s'avérer difficile. Sans une appropriation claire, le suivi des erreurs devient générique et souvent ignoré. Une solution consiste à demander aux ingénieurs de garde d'identifier quelle équipe doit répondre aux alarmes de surveillance. Cependant, une approche plus efficace consiste à inclure les informations sur le domaine et l’équipe dans chaque journal et étendue Datadog.
Pour savoir quelle équipe est responsable des différentes parties de notre application, nous utilisons un système appelé Domain Annotations. Les annotations de domaine étiquettent chaque partie du code de votre application, indiquant clairement qui est responsable de quoi. Cela fournit une organisation et une responsabilité claires dans la gestion des responsabilités.
Les annotations de domaine fournissent une méthode claire et organisée pour suivre les responsabilités de l'équipe au sein d'une application monolithique. En balisant des parties de votre code avec des annotations de domaine, vous pouvez :
Pour garantir une surveillance et une traçabilité efficaces, chaque requête Web est étiquetée avec les informations de domaine appropriées. Ceci est réalisé grâce à la collaboration de plusieurs composants : DomainProvider
, DomainSpanService
, DomainMdcProvider
et DomainHandlerInterceptor
.
Voici un aperçu général du processus décrit dans le diagramme suivant :
La mise en œuvre détaillée de ces composants sera encapsulée dans une bibliothèque partagée, fournissant une solution réutilisable pour le marquage et la surveillance des requêtes Web dans de grandes applications monolithiques.
Définir la propriété au niveau de la classe est simple avec les annotations de domaine. En appliquant des annotations de niveau supérieur aux classes principales, la propriété se propage à toutes les ressources détaillées de ces classes. Chaque équipe peut étiqueter les classes qu'elle possède avec les annotations de domaine appropriées, garantissant ainsi clarté et responsabilité sans avoir besoin de marquer chaque méthode.
Dans les cas où plusieurs équipes possèdent du code dans une classe et qu'une refactorisation immédiate n'est pas appropriée, vous pouvez marquer des méthodes individuelles avec différentes annotations de domaine, qui ont la priorité sur les annotations au niveau de la classe. Cela permet d'attribuer des méthodes spécifiques à différentes équipes, offrant ainsi de la flexibilité sans compliquer la structure globale.
Bien que les annotations de domaine soient incroyablement utiles, il existe de rares cas où elles ne peuvent pas être utilisées. Par exemple, nous avons rencontré des problèmes avec la création de tâches Quartz, qui ne fonctionnaient pas de manière transparente avec les annotations de domaine en raison d'un conflit entre la logique AOP de Quartz et la logique AOP utilisée pour les annotations de domaine.
Pour les tâches et les processus qui ne peuvent pas être annotés directement, nous avons utilisé le DomainTagsService directement dans les implémentations des tâches. Cette approche nous a permis d'ajouter manuellement des balises de domaine dans la logique d'exécution du travail.
Voici un exemple de la façon dont nous avons intégré DomainTagsService dans une tâche Quartz :
final override fun execute(context: JobExecutionContext) { domainTagsService.invoke(domain) { withLoggedExecutionDetails(context, ::doExecute) } }
Bien que le fait de disposer de services séparés pour chaque équipe offre des avantages significatifs en matière de surveillance et de propriété, cela entraîne des coûts et des efforts élevés pour diviser le monolithe, ainsi que d'éventuelles dépenses de développement supplémentaires. Compte tenu de la possibilité d'améliorer les temps de construction avec Gradle lorsque le monolithe est divisé en modules, le maintien d'un monorepo pourrait être la solution la plus efficace dans de nombreux cas.
Pour simplifier la surveillance des activités de chaque équipe dans Datadog, vous pouvez attribuer des noms de service artificiels aux différentes équipes. Cette approche garantit que chaque équipe dispose de sa propre section dédiée dans les outils de surveillance de Datadog. Bien que l’utilisation de noms de services artificiels puisse prêter à confusion si vous avez de nombreux services à gérer, cela devient gérable avec un nombre limité de services backend. L'ajout de préfixes à ces noms de services artificiels permet de maintenir l'organisation et la clarté de votre configuration Datadog, facilitant ainsi la distinction entre les différentes équipes et leurs responsabilités.
utiliser un diagramme au lieu de la capture d'écran ?? avoir un travailleur/une application Web n'a aucun sens ici
L'utilisation de noms de service artificiels pour les journaux peut créer de la confusion, car la même entrée de journal peut apparaître sous différents services.
Par exemple, considérons deux points de terminaison utilisant le même service d'authentification. Si ces points de terminaison sont annotés avec des domaines différents, la logique d'authentification produira des journaux sous différents services artificiels. Cela pourrait prêter à confusion lors de l'exploration des journaux, car ils apparaissent sous plusieurs noms de services. Pour éviter ce problème, il est préférable d'appliquer des noms de service artificiels uniquement aux étendues regroupées dans des traces afin d'éviter toute confusion.
Celà a-t-il un sens? je ne pense pas que ce soit le cas
Voici une représentation visuelle de ce problème :
L'utilisation de services artificiels vous permet non seulement de travailler avec des traces APM, mais également de filtrer par service dans Datadog Metrics, qui sont stockées pendant une période prolongée, permettant ainsi de suivre les modifications sur une période prolongée.
Vous trouverez ci-dessous une capture d'écran d'un moniteur dans Datadog qui utilise le nom de service artificiel konsus-assets
dans la requête :
Vous trouverez ci-dessous une capture d'écran d'un tableau de bord dans Datadog qui utilise le nom de service artificiel konsus-assets
dans le filtre :
En utilisant de faux services dans votre stratégie de surveillance, vous pouvez améliorer la visibilité et la responsabilité des activités de chaque équipe au sein d'une application monolithique. Cette approche simplifie le processus de création et de maintenance de moniteurs et de tableaux de bord spécifiques à une équipe, conduisant à une surveillance plus efficace et organisée dans Datadog.
Les annotations de domaine offrent une approche simple pour simplifier la surveillance des applications monolithiques dans Datadog. En mettant en œuvre cette stratégie, vous pouvez améliorer la gérabilité des journaux, des étendues et des métriques, transformant ainsi votre configuration de surveillance en un outil adapté à des équipes spécifiques. Cela améliore la responsabilité et l'organisation et facilite un dépannage et une analyse des performances plus efficaces et efficients dans l'ensemble de votre application.
DomainTagsService
directement dans les implémentations de tâches garantit que la surveillance spécifique au domaine peut toujours être maintenue.Définir des domaines et des équipes
Cela va changer avec la lib !!!
Créez des énumérations représentant différents domaines et équipes dans votre application :
@Domain
est une annotation qui peut être appliquée à des classes ou des fonctions, en les marquant avec une valeur de domaine spécifique.DomainValue
est une énumération représentant différents domaines, chacun associé à une équipe.Team
est une énumération représentant les différentes équipes travaillant sur l'application. @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) annotation class Domain(val value: DomainValue) enum class DomainValue(val team: Team) { USER_MANAGEMENT(Team.TEAM_A), PAYMENT_PROCESSING(Team.TEAM_B), NOTIFICATIONS(Team.TEAM_C) } enum class Team { TEAM_A, TEAM_B, TEAM_C }
Annoter les classes (et les méthodes si nécessaire)
@Domain(DomainValue.USER_MANAGEMENT) class UserService { @Domain(DomainValue.PAYMENT_PROCESSING) fun processPayment() { ... } }
Gérer les cas non pris en charge
Pour les cas qui ne peuvent pas être annotés directement, utilisez directement DomainTagsService
pour envelopper la logique
fun executeNotSupportedByAnnotationsLogic() { domainTagsService.invoke(domain) { executeLogic() } }
Surveiller avec Datadog
Utilisez des filtres de service artificiels pour les moniteurs, les tableaux de bord et le filtrage des traces APM
En suivant ces étapes, vous pouvez implémenter efficacement les annotations de domaine dans votre application monolithique, garantissant ainsi une surveillance, une responsabilité et une efficacité globale améliorées.