Surveillance en temps réel de Watermill à l'aide de Prometheus

Métriques

Watermill peut être surveillé en utilisant des décorateurs pour les éditeurs/abonnés et des intergiciels pour les gestionnaires. Nous fournissons une implémentation par défaut en utilisant la bibliothèque officielle client Prometheus pour Go.

Le package components/metrics exporte PrometheusMetricsBuilder, qui fournit des fonctions pratiques pour envelopper les éditeurs, les abonnés et les gestionnaires afin de mettre à jour le registre Prometheus pertinent :

Code source complet : github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// PrometheusMetricsBuilder fournit des méthodes pour décorer les éditeurs, les abonnés et les gestionnaires.
type PrometheusMetricsBuilder struct {
    // PrometheusRegistry peut remplir un registre Prometheus existant ou être vide pour utiliser le registre par défaut.
    PrometheusRegistry prometheus.Registerer

    Namespace string
    Subsystem string
}

// AddPrometheusRouterMetrics est une fonction pratique pour ajouter un intergiciel de métriques à tous les gestionnaires sur le routeur de messages. Il décore également les éditeurs et les abonnés des gestionnaires.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
// ...

Envelopper les éditeurs, les abonnés et les gestionnaires

Si vous utilisez le routeur de Watermill (ce qui est recommandé dans la plupart des cas), vous pouvez utiliser la fonction pratique AddPrometheusRouterMetrics pour vous assurer que tous les gestionnaires ajoutés à ce routeur sont enveloppés pour mettre à jour le registre Prometheus, ainsi que leurs éditeurs et abonnés :

Code source complet : github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// AddPrometheusRouterMetrics est une fonction pratique pour ajouter un intergiciel de métriques à tous les gestionnaires sur le routeur de messages. Il décore également les éditeurs et les abonnés des gestionnaires.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
    r.AddPublisherDecorators(b.DecoratePublisher)
    r.AddSubscriberDecorators(b.DecorateSubscriber)
    r.AddMiddleware(b.NewRouterMiddleware().Middleware)
}
// ...

Exemple d'utilisation de AddPrometheusRouterMetrics :

Code source complet : github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

// ...
    // Nous laissons les paramètres d'espace de noms et de sous-système vides
    metricsBuilder := metrics.NewPrometheusMetricsBuilder(registrePrometheus, "", "")
    metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

Dans l'extrait de code ci-dessus, nous laissons les paramètres namespace et subsystem vides. La bibliothèque cliente Prometheus utilise ces paramètres pour préfixer les noms des métriques. Vous pouvez vouloir utiliser l'espace de noms ou le sous-système, mais veuillez noter que cela affectera les noms des métriques, vous devrez donc ajuster le tableau de bord Grafana en conséquence.

Les éditeurs et abonnés indépendants peuvent également être décorés en utilisant des méthodes dédiées de PrometheusMetricsBuilder :

Code source complet : github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

// ...
    subAvecMetriques, err := metricsBuilder.DecorateSubscriber(pubSub)
    if err != nil {
        panic(err)
    }
    pubAvecMetriques, err := metricsBuilder.DecoratePublisher(pub)
    if err != nil {
        panic(err)
    }
// ...

Exposer le point de terminaison /metrics

Selon le principe de fonctionnement de Prometheus, un service doit exposer un point de terminaison HTTP pour le raclage des données. Conventionnellement, il s'agit d'un point de terminaison GET, et le chemin est généralement /metrics.

Pour fournir ce point de terminaison, il existe deux fonctions pratiques disponibles, l'une utilisant le registre Prometheus précédemment créé et l'autre créant simultanément un nouveau Registre :

Code source complet : github.com/ThreeDotsLabs/watermill/components/metrics/http.go

// ...
// CreateRegistryAndServeHTTP établit un serveur HTTP à l'adresse donnée pour exposer le point de terminaison /metrics à Prometheus.
// Il renvoie un nouveau Registre Prometheus (pour l'enregistrement des métriques) et une fonction d'annulation pour arrêter le serveur.
func CreateRegistryAndServeHTTP(addr string) (registry *prometheus.Registry, cancel func()) {
	registry = prometheus.NewRegistry()
	return registry, ServeHTTP(addr, registry)
}

// ServeHTTP établit un serveur HTTP à l'adresse donnée pour exposer le point de terminaison /metrics à Prometheus.
// Il accepte un Registre Prometheus existant et renvoie une fonction d'annulation pour arrêter le serveur.
func ServeHTTP(addr string, registry *prometheus.Registry) (cancel func()) {
// ...

Voici un exemple d'utilisation :

Code source complet : github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

// ...
	registrePrometheus, fermerServeurMetrics := metrics.CreateRegistryAndServeHTTP(*metricsAddr)
	defer fermerServeurMetrics()

	// Nous laissons l'espace de noms et le sous-système vides
	metricsBuilder := metrics.NewPrometheusMetricsBuilder(registrePrometheus, "", "")
	metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

Exemple d'Application

Pour comprendre le fonctionnement du tableau de bord en pratique, vous pouvez vous référer à l'exemple de métriques.

Suivez les instructions dans le README de l'exemple pour l'exécuter et ajouter la source de données Prometheus à Grafana.

Tableau de bord Grafana

Nous avons préparé un tableau de bord Grafana à utiliser avec la mise en oeuvre des métriques mentionnées. Il fournit des informations de base sur le débit, le taux d'échec et la durée de publication/traitement.

Si vous souhaitez consulter ce tableau de bord localement, vous pouvez utiliser l'application exemple.

Pour plus d'informations sur les métriques exportées vers Prometheus, voir Métriques exportées.

Importation du Tableau de bord

Pour importer le tableau de bord Grafana, sélectionnez Dashboard/Gérer dans le menu de gauche, puis cliquez sur +Import.

Entrez l'URL du tableau de bord https://grafana.com/dashboards/9777 (ou simplement l'identifiant, 9777), puis cliquez sur Charger.

Importer le Tableau de bord

Ensuite, sélectionnez la source de données Prometheus utilisée pour racler le point de terminaison /metrics. Cliquez sur Importer et le tour est joué !

Métriques exportées

Ce qui suit énumère toutes les métriques enregistrées dans le registre Prometheus par PrometheusMetricsBuilder.

Pour plus d'informations sur les types de métriques Prometheus, veuillez vous référer à la documentation de Prometheus.

Objet Métrique Description Libellés/Valeurs
Abonné subscriber_messages_received_total Un compteur Prometheus. Compte le nombre de messages reçus par un abonné. acked est "acked" ou "nacked".
Si l'abonné opère dans un gestionnaire, définir handler_name; sinon, "".
subscriber_name identifie l'abonné. S'il implémente l'interface fmt.Stringer, c'est le résultat de String(); sinon, c'est package.structName.
Gestionnaire handler_execution_time_seconds Un histogramme Prometheus. Enregistre le temps d'exécution de la fonction de gestionnaire enveloppée par le middleware. handler_name est le nom du gestionnaire.
success est "true" ou "false", selon que la fonction de gestionnaire enveloppée renvoie une erreur ou non.
Éditeur publish_time_seconds Un histogramme Prometheus. Enregistre le temps d'exécution de la fonction de publication décorée de l'éditeur. success est "true" ou "false", selon que l'éditeur décoré renvoie une erreur ou non.
Si l'éditeur opère dans un gestionnaire, définir handler_name; sinon, "".
publisher_name identifie l'éditeur. S'il implémente l'interface fmt.Stringer, c'est le résultat de String(); sinon, c'est package.structName.

De plus, chaque métrique a un libellé node fourni par Prometheus, avec sa valeur correspondant à l'instance de la source de la métrique, et un libellé job spécifié comme nom du travail dans le fichier de configuration de Prometheus.

Remarque : Comme mentionné ci-dessus, l'utilisation d'un namespace ou subsystem non vide entraînera un préfixe de nom de métrique. Vous devrez peut-être apporter des ajustements correspondants, comme dans la définition de panneau d'un tableau de bord Grafana.

Personnalisation

Si vous estimez qu'une certaine métrique a été négligée, vous pouvez facilement étendre cette implémentation de base. La meilleure façon est d'utiliser le registre Prometheus utilisé avec la méthode ServeHTTP et d'enregistrer les métriques selon la documentation du client Prometheus ici.

Une méthode concise pour mettre à jour ces métriques consiste à utiliser des décorateurs. Le code source complet peut être trouvé à l'adresse github.com/ThreeDotsLabs/watermill/message/decorator.go.

// ...
// MessageTransformSubscriberDecorator crée un décorateur d'abonné qui appelle la fonction de transformation sur chaque message passée par l'abonné.
func MessageTransformSubscriberDecorator(transform func(*Message)) SubscriberDecorator {
	if transform == nil {
		panic("la fonction de transformation est nulle")
	}
	return func(sub Subscriber) (Subscriber, error) {
		return &messageTransformSubscriberDecorator{
			sub:       sub,
			transform: transform,
		}, nil
	}
}

// MessageTransformPublisherDecorator crée un décorateur d'éditeur qui appelle la fonction de transformation sur chaque message passée par l'éditeur.
func MessageTransformPublisherDecorator(transform func(*Message)) PublisherDecorator {
	if transform == nil {
		panic("la fonction de transformation est nulle")
	}
	return func(pub Publisher) (Publisher, error) {
		return &messageTransformPublisherDecorator{
			Publisher: pub,
			transform: transform,
		}, nil
	}
}

type messageTransformSubscriberDecorator struct {
// ...

Et/ou des middleware dans les routeurs.

Une méthode plus simple consiste à mettre à jour les métriques requises uniquement dans les fonctions de gestionnaire.