
Nous utilisons une architecture microservice dans nos projets. Lorsque des goulots d'étranglement de performances se produisent, beaucoup de temps est consacré à la surveillance et à l'analyse des journaux. Lors de la journalisation des minutages d'opérations individuelles dans un fichier journal, il est généralement difficile de comprendre ce qui a conduit à l'invocation de ces opérations, de suivre la séquence d'actions ou le décalage temporel d'une opération par rapport à une autre dans différents services.
Pour minimiser le travail manuel, nous avons décidé d'utiliser l'un des outils de traçage. Comment et pourquoi vous pouvez utiliser le traçage et comment nous l'avons fait, et seront discutés dans cet article.
Quels problĂšmes peuvent ĂȘtre rĂ©solus avec le traçage
- Trouvez les goulots d'étranglement des performances à la fois au sein d'un service unique et dans l'ensemble de l'arborescence d'exécution entre tous les services participants. Par exemple:
- De nombreux appels courts et consécutifs entre services, par exemple vers un géocodage ou vers une base de données.
- Attentes d'E/S longues, telles que les transferts réseau ou les lectures de disque.
- Longue analyse de données.
- Opérations longues nécessitant CPU.
- Les sections de code qui ne sont pas nĂ©cessaires pour obtenir le rĂ©sultat final et qui peuvent ĂȘtre supprimĂ©es ou retardĂ©es.
- Comprenez clairement dans quel ordre ce qui est appelé et ce qui se passe lorsque l'opération est effectuée.

On peut voir que, par exemple, la requĂȘte est arrivĂ©e au service WS -> le service WS a ajoutĂ© des donnĂ©es via le service R -> puis a envoyĂ© une requĂȘte au service V -> le service V a chargĂ© beaucoup de donnĂ©es du R service -> est allĂ© au service P -> le service P est allĂ© Ă nouveau au service R -> le service V a ignorĂ© le rĂ©sultat et est allĂ© au service J -> et n'a renvoyĂ© qu'ensuite la rĂ©ponse au service WS, tout en continuant Ă calculer autre chose dans le arriĂšre-plan.
Sans une telle trace ou une documentation détaillée pour l'ensemble du processus, il est trÚs difficile de comprendre ce qui se passe lorsque l'on regarde le code pour la premiÚre fois, et le code est dispersé sur différents services et caché derriÚre un tas de bacs et d'interfaces. - Collecte d'informations sur l'arbre d'exécution pour une analyse différée ultérieure. à chaque étape de l'exécution, vous pouvez ajouter des informations à la trace disponible à ce stade, puis déterminer quelles données d'entrée ont conduit à un scénario similaire. Par exemple:
- ID de l'utilisateur
- Droits
- Type de méthode sélectionnée
- Journal ou erreur d'exécution
- Transformer les traces en un sous-ensemble de métriques et une analyse plus approfondie déjà sous la forme de métriques.
Quelle trace peut enregistrer. Portée
Dans le traçage, il y a le concept d'une portée, c'est un analogue d'un journal, à la console. Le spa dispose de :
- Nom, généralement le nom de la méthode qui a été exécutée
- Le nom du service dans lequel le span a été généré
- Propre identifiant unique
- Une sorte de méta-information sous la forme d'une clé/valeur qui y a été connectée. Par exemple, les paramÚtres de la méthode ou la méthode s'est terminée par une erreur ou non
- Heures de début et de fin pour cette période
- ID d'étendue parent
Chaque span est envoyĂ© au collecteur de span pour ĂȘtre stockĂ© dans la base de donnĂ©es pour un examen ultĂ©rieur dĂšs qu'il a terminĂ© son exĂ©cution. Ă l'avenir, vous pourrez crĂ©er une arborescence de toutes les Ă©tendues en vous connectant par identifiant parent. Lors de l'analyse, vous pouvez trouver, par exemple, toutes les durĂ©es d'un service qui ont pris plus d'un certain temps. De plus, en allant Ă une Ă©tendue spĂ©cifique, voir l'arbre entier au-dessus et en dessous de cette Ă©tendue.

Opentrace, Jagger et comment nous l'avons implémenté pour nos projets
Il existe une norme commune , qui dĂ©crit comment et ce qui doit ĂȘtre collectĂ©, sans ĂȘtre liĂ© par un traçage Ă une implĂ©mentation spĂ©cifique dans une langue. Par exemple, en Java, tout travail avec des traces est effectuĂ© via l'API Opentrace commune, et sous celle-ci, par exemple, Jaeger ou une implĂ©mentation par dĂ©faut vide qui ne fait rien peut ĂȘtre masquĂ©e.
Nous utilisons en tant qu'implémentation d'Opentrace. Il se compose de plusieurs composants :

- Jaeger-agent est un agent local qui est généralement installé sur chaque machine et les services y sont connectés sur le port local par défaut. S'il n'y a pas d'agent, les traces de tous les services sur cette machine sont généralement désactivées
- Jaeger-collector - tous les agents lui envoient les traces collectées, et il les place dans la base de données sélectionnée
- La base de données est leur cassandra préférée, mais nous utilisons elasticsearch, il existe des implémentations pour quelques autres bases de données et une implémentation en mémoire qui n'enregistre rien sur le disque
- Jaeger-query est un service qui va à la base de données et renvoie les traces déjà collectées pour analyse
- Jaeger-ui est une interface web pour rechercher et visualiser des traces, il va Ă jaeger-query

Un composant distinct peut ĂȘtre appelĂ© l'implĂ©mentation d'opentrace jaeger pour des langages spĂ©cifiques, Ă travers lequel les Ă©tendues sont envoyĂ©es Ă jaeger-agent.
revient à implémenter l'interface io.opentracing.Tracer, aprÚs quoi toutes les traces qui la traversent voleront vers l'agent réel.

Ăgalement pour le composant de ressort, vous pouvez connecter et mise en Ćuvre de Jaeger qui configurera automatiquement le traçage pour tout ce qui passe par ces composants, par exemple les requĂȘtes http aux contrĂŽleurs, les requĂȘtes Ă la base de donnĂ©es via jdbc, etc.
Journalisation des traces en Java
Quelque part au niveau supĂ©rieur, le premier Span doit ĂȘtre créé, cela peut ĂȘtre fait automatiquement, par exemple, par le contrĂŽleur de ressort lorsqu'une demande est reçue, ou manuellement s'il n'y en a pas. Il est ensuite transmis via le champ d'application ci-dessous. Si l'une des mĂ©thodes ci-dessous souhaite ajouter un Span, elle prend l'activeSpan actuel du Scope, crĂ©e un nouveau Span et indique que son parent est l'activeSpan rĂ©sultant, et active le nouveau Span. Lors de l'appel de services externes, la plage active actuelle leur est transmise et ces services crĂ©ent de nouvelles plages en rĂ©fĂ©rence Ă cette plage.
Tout le travail passe par l'instance Tracer, vous pouvez l'obtenir via le mécanisme DI, ou GlobalTracer.get () en tant que variable globale si le mécanisme DI ne fonctionne pas. Par défaut, si le traceur n'a pas été initialisé, NoopTracer retournera ce qui ne fait rien.
De plus, la portée actuelle est obtenue à partir du traceur via le ScopeManager, une nouvelle portée est créée à partir de la portée actuelle avec une liaison de la nouvelle portée, puis la portée créée est fermée, ce qui ferme la portée créée et renvoie la portée précédente à l'état actif. La portée est liée à un thread, donc lors de la programmation multi-thread, vous ne devez pas oublier de transférer la plage active vers un autre thread, pour une activation ultérieure de la portée d'un autre thread en référence à cette plage.
io.opentracing.Tracer tracer = ...; // GlobalTracer.get()
void DoSmth () {
try (Scope scope = tracer.buildSpan("DoSmth").startActive(true)) {
...
}
}
void DoOther () {
Span span = tracer.buildSpan("someWork").start();
try (Scope scope = tracer.scopeManager().activate(span, false)) {
// Do things.
} catch(Exception ex) {
Tags.ERROR.set(span, true);
span.log(Map.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage()));
} finally {
span.finish();
}
}
void DoAsync () {
try (Scope scope = tracer.buildSpan("ServiceHandlerSpan").startActive(false)) {
...
final Span span = scope.span();
doAsyncWork(() -> {
// STEP 2 ABOVE: reactivate the Span in the callback, passing true to
// startActive() if/when the Span must be finished.
try (Scope scope = tracer.scopeManager().activate(span, false)) {
...
}
});
}
}Pour la programmation multi-thread, il existe également TracedExecutorService et des wrappers similaires qui transfÚrent automatiquement l'étendue actuelle au thread lorsque des tùches asynchrones sont lancées :
private ExecutorService executor = new TracedExecutorService(
Executors.newFixedThreadPool(10), GlobalTracer.get()
);Pour les requĂȘtes http externes, il y a
HttpClient httpClient = new TracingHttpClientBuilder().build();ProblÚmes auxquels nous avons été confrontés
- Les beans et DI ne fonctionnent pas toujours si le traceur n'est pas utilisé dans un service ou un composant, alors Tracer peut ne pas fonctionner et vous devrez utiliser GlobalTracer.get().
- Les annotations ne fonctionnent pas s'il ne s'agit pas d'un composant ou d'un service, ou si la mĂ©thode est appelĂ©e depuis une mĂ©thode voisine de la mĂȘme classe. Vous devez faire attention Ă vĂ©rifier ce qui fonctionne et utiliser la crĂ©ation de trace manuelle si @Traced ne fonctionne pas. Vous pouvez Ă©galement joindre un compilateur supplĂ©mentaire pour les annotations Java, alors elles devraient fonctionner partout.
- Dans l'ancien démarrage du printemps et du printemps, la configuration automatique du nuage de printemps opentraing ne fonctionne pas en raison de bogues dans DI, alors si vous voulez que les traces dans les composants du printemps fonctionnent automatiquement, vous pouvez le faire par analogie avec
- Try with resources ne fonctionne pas dans groovy, vous devez utiliser try finally.
- Chaque service doit avoir son propre nom spring.application.name sous lequel les traces seront enregistrĂ©es. Qu'est-ce qu'un nom distinct pour la vente et le test, afin de ne pas les gĂȘner ensemble.
- Si vous utilisez GlobalTracer et tomcat, tous les services exĂ©cutĂ©s dans ce tomcat ont un GlobalTracer, ils auront donc tous le mĂȘme nom de service.
- Lorsque vous ajoutez des traces à une méthode, vous devez vous assurer qu'elle n'est pas appelée plusieurs fois dans une boucle. Il est nécessaire d'ajouter une trace commune pour tous les appels, ce qui garantit le temps de travail total. Sinon, une surcharge sera créée.
- Une fois dans jaeger-ui, des requĂȘtes trop importantes Ă©taient faites pour un grand nombre de traces, et comme ils n'attendaient pas de rĂ©ponse, ils recommençaient. En consĂ©quence, Jaeger-Query a commencĂ© Ă consommer beaucoup de mĂ©moire et Ă ralentir Elastic. AidĂ© en redĂ©marrant jaeger-query
Ăchantillonnage, stockage et visualisation des traces
Il existe trois types :
- Const qui envoie et enregistre toutes les traces.
- Probabiliste qui filtre les traces avec une probabilité donnée.
- Ratelimiting qui limite le nombre de traces par seconde. Vous pouvez configurer ces paramĂštres sur le client, soit sur l'agent jaeger, soit sur le collecteur. Maintenant, nous utilisons const 1 dans la pile de valuateurs, car il n'y a pas beaucoup de requĂȘtes, mais elles prennent beaucoup de temps. Ă l'avenir, si cela exerce une charge excessive sur le systĂšme, vous pouvez la limiter.
Si vous utilisez cassandra, par défaut, il ne stocke les traces que pendant deux jours. Nous utilisons et les traces sont stockées pour toujours et ne sont pas supprimées. Un index distinct est créé pour chaque jour, par exemple jaeger-service-2019-03-04. à l'avenir, vous devrez configurer le nettoyage automatique des anciennes traces.
Pour visualiser les traces dont vous avez besoin :
- Sélectionnez le service par lequel vous souhaitez filtrer les traces, par exemple, tomcat7-default pour un service qui s'exécute dans le tomcat et ne peut pas avoir son propre nom.
- Sélectionnez ensuite l'opération, l'intervalle de temps et le temps d'opération minimum, par exemple à partir de 10 secondes, pour ne prendre que des exécutions longues.

- Allez Ă l'une des traces et voyez ce qui ralentissait lĂ -bas.

De plus, si un identifiant de requĂȘte est connu, vous pouvez trouver une trace par cet identifiant via une recherche de balise, si cet identifiant est enregistrĂ© dans la plage de trace.
Documentation
- documentation opentracing
- documentation jÀger
- Connexion Java Jaeger
- connexion de traçage à ressort ouvert
Articles
- Jaeger Opentracing et Microservices dans un vrai projet PHP et Golang
- Ăvolution du traçage distribuĂ© chez Uber Engineering
- Exécution de Jaeger Agent sur du métal nu
Vidéos
- Comment nous avons utilisĂ© Jaeger et Prometheus pour fournir des requĂȘtes utilisateur ultra-rapides â Bryan Boreham
- Intro : Jaeger - Yuri Shkuro, Uber & Pavol Loffay, Red Hat
- Serghei Iakovlev, « Une petite histoire d'une grande victoire : OpenTracing, AWS et Jaeger »
Source: habr.com



