Monitoraggio in tempo reale di Watermill utilizzando Prometheus

Metriche

Watermill può essere monitorato utilizzando i decoratori per i publisher/subscriber e i middleware per gli handler. Forniamo un'implementazione predefinita utilizzando il client Prometheus ufficiale per Go.

Il pacchetto components/metrics esporta PrometheusMetricsBuilder, che fornisce funzioni comode per incapsulare i publisher, i subscriber e gli handler per aggiornare il registro di Prometheus pertinente:

Codice sorgente completo: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// Il PrometheusMetricsBuilder fornisce metodi per decorare publisher, subscriber e handler.
type PrometheusMetricsBuilder struct {
    // PrometheusRegistry può riempire un registro Prometheus esistente o essere vuoto per utilizzare il registro predefinito.
    PrometheusRegistry prometheus.Registerer

    Namespace string
    Sottosistema string
}

// AddPrometheusRouterMetrics è una funzione comoda per aggiungere un middleware metrico a tutti gli handler sul message router. Decorerà anche i publisher e i subscriber degli handler.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
// ...

Incapsulamento di Publisher, Subscriber e Handler

Se si sta utilizzando il router di Watermill (che è consigliato nella maggior parte dei casi), è possibile utilizzare la funzione comoda AddPrometheusRouterMetrics per garantire che tutti gli handler aggiunti a questo router siano incapsulati per aggiornare il registro di Prometheus, così come i loro publisher e subscriber:

Codice sorgente completo: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// AddPrometheusRouterMetrics è una funzione comoda per aggiungere un middleware metrico a tutti gli handler sul message router. Decorerà anche i publisher e i subscriber degli handler.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
    r.AddPublisherDecorators(b.DecoratePublisher)
    r.AddSubscriberDecorators(b.DecorateSubscriber)
    r.AddMiddleware(b.NewRouterMiddleware().Middleware)
}
// ...

Esempio di utilizzo di AddPrometheusRouterMetrics:

Codice sorgente completo: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

// ...
    // Lasciamo vuoti i parametri di namespace e sottosistema
    metricsBuilder := metrics.NewPrometheusMetricsBuilder(registroPrometheus, "", "")
    metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

Nel frammento di codice sopra, lasciamo vuoti i parametri namespace e sottosistema. La libreria client di Prometheus utilizza questi parametri per prefissare i nomi delle metriche. Potresti voler utilizzare il namespace o il sottosistema, ma per favore nota che questo influenzerà i nomi delle metriche, quindi è necessario aggiornare di conseguenza il cruscotto di Grafana.

I publisher e i subscriber indipendenti possono anche essere decorati utilizzando metodi dedicati di PrometheusMetricsBuilder:

Codice sorgente completo: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

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

Esposizione del punto finale /metrics

Secondo il principio di funzionamento di Prometheus, un servizio deve esporre un punto finale HTTP per l'estrazione dei dati. Convenzionalmente, si tratta di un punto finale GET, e il percorso è di solito /metrics.

Per fornire questo punto finale, ci sono due funzioni convenienti disponibili, una che utilizza il Registro Prometheus precedentemente creato e l'altra che crea contemporaneamente un nuovo Registro:

Codice sorgente completo: github.com/ThreeDotsLabs/watermill/components/metrics/http.go

// ...
// CreateRegistryAndServeHTTP stabilisce un server HTTP all'indirizzo specificato per esporre il punto finale /metrics a Prometheus.
// Restituisce un nuovo Registro Prometheus (per la registrazione della metrica) e una funzione di cancellazione per arrestare il server.
func CreateRegistryAndServeHTTP(addr string) (registry *prometheus.Registry, cancel func()) {
	registry = prometheus.NewRegistry()
	return registry, ServeHTTP(addr, registry)
}

// ServeHTTP stabilisce un server HTTP all'indirizzo specificato per esporre il punto finale /metrics a Prometheus.
// Accetta un Registro Prometheus esistente e restituisce una funzione di cancellazione per arrestare il server.
func ServeHTTP(addr string, registry *prometheus.Registry) (cancel func()) {
// ...

Ecco un esempio di utilizzo:

Codice sorgente completo: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

// ...
	registryPrometeus, chiudiServerMetriche := metrics.CreateRegistryAndServeHTTP(*indirizzoMetriche)
	defer chiudiServerMetriche()

	// Lasciamo vuoti il namespace e il sottosistema
	builderMetriche := metrics.NewPrometheusMetricsBuilder(registryPrometeus, "", "")
	builderMetriche.AggiungiMetricheRouterPrometeus(router)
// ...

Applicazione di esempio

Per capire come funziona il cruscotto nella pratica, è possibile fare riferimento all'esempio delle metriche.

Seguire le istruzioni nel README dell'esempio per eseguirlo e aggiungere la fonte dati Prometheus a Grafana.

Cruscotto di Grafana

Abbiamo preparato un cruscotto di Grafana da utilizzare con l'implementazione delle metriche sopra menzionata. Fornisce informazioni di base sulla capacità, il tasso di fallimento e la durata della pubblicazione/elaborazione.

Se desideri visualizzare questo cruscotto localmente, puoi utilizzare l'applicazione di esempio.

Per ulteriori informazioni sulle metriche esportate a Prometheus, consulta le Metriche esportate.

Importazione del cruscotto

Per importare il cruscotto di Grafana, seleziona Dashboard/Gestisci dal menu di sinistra, quindi fai clic su +Import.

Inserisci l'URL del cruscotto https://grafana.com/dashboards/9777 (o solo l'ID, 9777), quindi fai clic su Carica.

Importa cruscotto

Successivamente, seleziona la fonte dati Prometheus utilizzata per estrarre il punto finale /metrics. Fai clic su Importa e hai finito!

Metriche esportate

Di seguito sono elencate tutte le metriche registrate nel registro di Prometheus da PrometheusMetricsBuilder.

Per maggiori informazioni sui tipi di metriche di Prometheus, fare riferimento alla documentazione di Prometheus.

Oggetto Metrica Descrizione Etichette/Valori
Subscriber subscriber_messages_received_total Un contatore Prometheus. Conta il numero di messaggi ricevuti da un subscriber. acked è "acked" o "nacked".
Se il subscriber opera all'interno di un handler, impostare handler_name; altrimenti, "".
subscriber_name identifica il subscriber. Se implementa l'interfaccia fmt.Stringer, è il risultato di String(); altrimenti, è package.structName.
Handler handler_execution_time_seconds Un istogramma Prometheus. Registra il tempo di esecuzione della funzione dell'handler avvolta dal middleware. handler_name è il nome dell'handler.
success è "true" o "false", a seconda che la funzione dell'handler avvolta restituisca un errore.
Publisher publish_time_seconds Un istogramma Prometheus. Registra il tempo di esecuzione della funzione di pubblicazione decorata del publisher. success è "true" o "false", a seconda che il publisher decorato restituisca un errore.
Se il publisher opera all'interno di un handler, impostare handler_name; altrimenti, "".
publisher_name identifica il publisher. Se implementa l'interfaccia fmt.Stringer, è il risultato di String(); altrimenti, è package.structName.

Inoltre, ciascuna metrica ha un'etichetta node fornita da Prometheus, con il suo valore corrispondente all'istanza della fonte della metrica, e un job specificato come nome del job nel file di configurazione di Prometheus.

Nota: Come precedentemente menzionato, l'utilizzo di namespace o subsystem non vuoti comporterà un prefisso nel nome della metrica. Potrebbe essere necessario apportare eventuali modifiche corrispondenti, ad esempio nella definizione del pannello di un dashboard di Grafana.

Personalizzazione

Se si crede che una certa metrica sia stata trascurata, è possibile estendere facilmente questa implementazione di base. Il modo migliore è utilizzare il registro di Prometheus utilizzato con il metodo ServeHTTP e registrare le metriche in base alla documentazione del client Prometheus qui.

Un metodo conciso per aggiornare queste metriche è utilizzare i decorator. Il codice sorgente completo è disponibile su github.com/ThreeDotsLabs/watermill/message/decorator.go.

// ...
// MessageTransformSubscriberDecorator crea un decorator del subscriber che chiama la funzione di trasformazione su ciascun messaggio passato attraverso il subscriber.
func MessageTransformSubscriberDecorator(transform func(*Message)) SubscriberDecorator {
	if transform == nil {
		panic("funzione di trasformazione è nulla")
	}
	return func(sub Subscriber) (Subscriber, error) {
		return &messageTransformSubscriberDecorator{
			sub:       sub,
			transform: transform,
		}, nil
	}
}

// MessageTransformPublisherDecorator crea un decorator del publisher che chiama la funzione di trasformazione su ciascun messaggio passato attraverso il publisher.
func MessageTransformPublisherDecorator(transform func(*Message)) PublisherDecorator {
	if transform == nil {
		panic("funzione di trasformazione è nulla")
	}
	return func(pub Publisher) (Publisher, error) {
		return &messageTransformPublisherDecorator{
			Publisher: pub,
			transform: transform,
		}, nil
	}
}

type messageTransformSubscriberDecorator struct {
// ...

E/o middleware nei router.

Un metodo più semplice è quello di aggiornare solo le metriche richieste nelle funzioni dell'handler.