Maison > Article > développement back-end > Utiliser le modèle Heartbeats dans Golang
Au cours de mes aventures en équilibrage Data & Software Engineer, je cherche toujours quelque chose d'un peu différent dans GoLang pour étudier, comprendre son fonctionnement et l'appliquer à des choses plus complexes que certains cours et articles traditionnels de base que je trouve sur Internet. . Dans ce court article, je vais rapporter et démontrer comment j'ai implémenté via Go Routines, le package time utilisant Ticker pour simuler le battement de coeur ("Je suis vivant") de l'application, en plus de l'utilisation des canaux, etc.
Ce n'est pas une nouveauté pour beaucoup qu'il soit extrêmement important de s'assurer que quiconque appelle une certaine fonction sache si la fonction prend du temps, est en cours de traitement ou est verrouillée. Cela dit, plusieurs autres terminologies ont émergé telles que Trace, Metrics, connectivité, etc., qui ont été introduites dans les applications de surveillance qui utilisent dans la plupart des cas des agents installés sur les serveurs d'applications qui collectent les métriques et les envoient à des interfaces qui visualisent toutes (ou presque) le statut de votre candidature. Parmi ces outils nous avons DataDog, NewRelic, Slack, Grafana, Jaeger, etc.
Alors que j'étudie et réfléchis à la création de quelque chose de rapide et simple qui aborde des concepts Go plus avancés, j'ai créé une application relativement simple qui utilise le modèle de battements de cœur. Celui qui m'appelle reçoit le résultat et, en même temps, l'information si je suis toujours actif ou non. Dans un scénario plus avancé, cela peut être intéressant pour personnaliser ce qui est réellement une application active en fonction de certaines particularités métier, puisqu'une simple implémentation d'un Prometheus résout ce cas (l'application est-elle active ? CPU, Mémoire, goroutines ouvertes), mais pas avec des commentaires simultanés et personnalisables.
En termes de structure, je n'ai créé que trois fichiers au sein de mon package avec go mod :
Cette partie du code Go définit une variable appelée « dictionnaire » qui est une carte qui associe des caractères de type rune à des chaînes.
Chaque entrée de carte est une clé (rune) et une valeur (chaîne). Dans l'exemple ci-dessous, les touches sont des lettres minuscules de l'alphabet et les valeurs sont des noms associés à chaque lettre. Par exemple, la lettre « a » est associée au nom « airton », la lettre « b » est associée au nom « bruno », et ainsi de suite :
package heartbeat var dicionario = map[rune]string{ 'a': "airton", 'b': "bruno", 'c': "carlos", 'd': "daniel", 'e': "eduardo", 'f': "felipe", 'g': "gustavo", }
J'explique mieux ci-dessous après le code complet chaque partie du code :
package heartbeat import ( "context" "fmt" "time" ) func ProcessingTask( ctx context.Context, letras chan rune, interval time.Duration, ) (<-chan struct{}, <-chan string) { heartbeats := make(chan struct{}, 1) names := make(chan string) go func() { defer close(heartbeats) defer close(names) beat := time.NewTicker(interval) defer beat.Stop() for letra := range letras { select { case <-ctx.Done(): return case <-beat.C: select { case heartbeats <- struct{}{}: default: } case names <- dicionario[letra]: lether := dicionario[letra] fmt.Printf("Letra: %s \n", lether) time.Sleep(3 * time.Second) // Simula um tempo de espera para vermos o hearbeats } } }() return heartbeats, names }
package heartbeat import ( "context" "fmt" "time" )
Ici, j'ai mon package Heartbeat qui sera chargé d'implémenter une fonctionnalité qui envoie des « battements de cœur » à un intervalle de temps spécifique, lors du traitement des tâches. Pour cela, j'ai besoin de contexte (Context Management), de fmt (pour le formatage des chaînes) et de time pour le contrôle du temps.
func ProcessingTask ( ctx context.Context, letras chan rune, interval time.Duration, ) (<-chan struct{}, <-chan string) {
C'est la définition de la fonction ProcessingTask qui prend un contexte ctx, un canal de lettres (un canal qui reçoit des caractères Unicode) et un intervalle de temps comme arguments. La fonction renvoie deux canaux : un canal de battements de cœur qui envoie une structure vide pour chaque « battement de cœur » et un canal de noms qui envoie le nom de la lettre correspondant à chaque caractère reçu.
heartbeats := make(chan struct{}, 1) names := make(chan string)
Ces deux lignes créent deux canaux : heartbeats est un canal tampon d'une capacité d'un élément et names est un canal sans tampon.
go func() defer close(heartbeats) defer close(names) beat := time.NewTicker(interval) defer beat.Stop() for letra := range letras { select { case <-ctx.Done(): return case <-beat.C: select { case heartbeats <- struct{}{}: default: } case names <- dicionario[letra]: lether := dicionario[letra] fmt.Printf("Letra: %s \n", lether) time.Sleep(3 * time.Second) // Simula um tempo de espera para vermos o hearbeats } } }() return heartbeats, names
Il s'agit d'une goroutine anonyme (ou fonction anonyme qui s'exécute dans un nouveau thread) qui exécute la logique principale de la fonction ProcessingTask. Il utilise une boucle for-range pour lire les caractères du canal des lettres. Dans la boucle, utilisez une sélection pour choisir une action à effectuer parmi les options disponibles :
Enfin, la fonction renvoie les battements de cœur et nomme les canaux.
task_test.go
package heartbeat var dicionario = map[rune]string{ 'a': "airton", 'b': "bruno", 'c': "carlos", 'd': "daniel", 'e': "eduardo", 'f': "felipe", 'g': "gustavo", }
Ici, j'ai créé un test unitaire Go pour la fonction ProcessingTask qui a été expliquée précédemment. La fonction de test TestProcessingTask crée un contexte avec un délai d'attente de 20 secondes et un canal de caractères Unicode (lettres). La goroutine anonyme envoie ensuite les paroles au canal des paroles. La fonction ProcessingTask est ensuite appelée avec le contexte, le canal de caractères Unicode et un intervalle de temps. Il renvoie deux canaux, un canal de battement de cœur et un canal de mots.
La fonction de test exécute ensuite une boucle infinie avec une sélection, qui lit à partir de trois canaux : le contexte, le canal de battement de cœur et le canal de mots.
Si le contexte est annulé, la boucle de test est terminée. Si un battement de cœur est reçu, un message « Application Up ! » est imprimé sur une sortie standard. Si un mot est reçu, le test vérifie si le mot est présent dans le dictionnaire de lettres. S'il n'est pas présent, le test échoue et un message d'erreur s'affiche.
Par conséquent, ce test unitaire teste notre fonction ProcessingTask, qui reçoit des caractères d'un canal, envoie des noms de lettres à un autre canal et émet les « battements de cœur » lors de son exécution dans un contexte dans lequel j'ai utilisé une limite de temps. Ahhh... et il vérifie aussi si les noms des lettres envoyées au canal de mots sont présents dans le dictionnaire.
Ce code Go illustre quelques concepts importants du langage Go et des tests unitaires :
Projet complet sur mon GitHub : https://github.com/AirtonLira/heartbeatsGolang
LinkedIn - Airton Lira Junior
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!