Rzeczywisty monitoring Watermill przy użyciu Prometheus
Metryki
Watermill może być monitorowany za pomocą dekoratorów dla wydawców/subskrybentów oraz oprogramowania pośredniczącego dla handlerów. Udostępniamy domyślną implementację przy użyciu oficjalnego klienta Prometheusa dla języka Go.
Pakiet components/metrics
eksportuje PrometheusMetricsBuilder
, który udostępnia wygodne funkcje do opakowywania wydawców, subskrybentów i handlerów w celu zaktualizowania odpowiedniego rejestru Prometheusa:
Pełen kod źródłowy: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go
// ...
// PrometheusMetricsBuilder udostępnia metody do dekorowania wydawców, subskrybentów i handlerów.
type PrometheusMetricsBuilder struct {
// PrometheusRegistry może wypełnić istniejący rejestr Prometheusa lub być pusty, aby użyć domyślnego rejestru.
PrometheusRegistry prometheus.Registerer
Namespace string
Subsystem string
}
// AddPrometheusRouterMetrics to wygodna funkcja do dodawania oprogramowania pośredniczącego metryk do wszystkich handlerów na routera wiadomości. Dekoruje również wydawców i subskrybentów tych handlerów.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
// ...
Opakowywanie Wydawców, Subskrybentów i Handlerów
Jeśli używasz routera Watermill (co zalecamy w większości przypadków), możesz użyć wygodnej funkcji AddPrometheusRouterMetrics
, aby upewnić się, że wszystkie handlery dodane do tego routera są opakowane w celu zaktualizowania rejestru Prometheusa, a także ich wydawców i subskrybentów:
Pełen kod źródłowy: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go
// ...
// AddPrometheusRouterMetrics to wygodna funkcja do dodawania oprogramowania pośredniczącego metryk do wszystkich handlerów na routera wiadomości. Dekoruje również wydawców i subskrybentów tych handlerów.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
r.AddPublisherDecorators(b.DecoratePublisher)
r.AddSubscriberDecorators(b.DecorateSubscriber)
r.AddMiddleware(b.NewRouterMiddleware().Middleware)
}
// ...
Przykład korzystania z AddPrometheusRouterMetrics
:
Pełen kod źródłowy: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go
// ...
// Pozostawiamy puste parametry przestrzeni nazw i podsystemu
metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "")
metricsBuilder.AddPrometheusRouterMetrics(router)
// ...
W powyższym kawałku kodu pozostawiamy parametry przestrzeni nazw
i podsystemu
puste. Biblioteka klienta Prometheusa wykorzystuje te parametry do prefiksowania nazw metryk. Możesz chcieć użyć przestrzeni nazw lub podsystemu, ale należy pamiętać, że będzie to miało wpływ na nazwy metryk, dlatego konieczne będzie dostosowanie panelu Grafany.
Niezależni wydawcy i subskrybenci mogą również być dekorowani przy użyciu dedykowanych metod PrometheusMetricsBuilder
:
Pełen kod źródłowy: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go
// ...
subWithMetrics, err := metricsBuilder.DecorateSubscriber(pubSub)
if err != nil {
panic(err)
}
pubWithMetrics, err := metricsBuilder.DecoratePublisher(pub)
if err != nil {
panic(err)
}
// ...
Eksponowanie punktu końcowego /metrics
Zgodnie z zasadą działania systemu Prometheus, usługa musi udostępnić punkt końcowy HTTP do zbierania danych. Z reguły jest to punkt końcowy typu GET, a ścieżka zwykle to /metrics
.
Aby udostępnić ten punkt końcowy, dostępne są dwie wygodne funkcje, jedna korzystająca z wcześniej utworzonego Rejestru Prometheus, a druga jednocześnie tworząca nowy Rejestr:
Pełny kod źródłowy: github.com/ThreeDotsLabs/watermill/components/metrics/http.go
// ...
// CreateRegistryAndServeHTTP ustanawia serwer HTTP pod podanym adresem w celu udostępnienia punktu końcowego /metrics dla systemu Prometheus.
// Zwraca nowy Rejestr Prometheus (do rejestracji metryk) oraz funkcję anulowania w celu wyłączenia serwera.
func CreateRegistryAndServeHTTP(addr string) (rejestr *prometheus.Registry, anuluj func()) {
rejestr = prometheus.NewRegistry()
return rejestr, ServeHTTP(addr, rejestr)
}
// ServeHTTP ustanawia serwer HTTP pod podanym adresem w celu udostępnienia punktu końcowego /metrics dla systemu Prometheus.
// Akceptuje istniejący Rejestr Prometheus i zwraca funkcję anulowania w celu wyłączenia serwera.
func ServeHTTP(addr string, rejestr *prometheus.Registry) (anuluj func()) {
// ...
Oto przykład użycia:
Pełny kod źródłowy: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go
// ...
rejestrPrometheus, zamknijSerwerMetryk := metrics.CreateRegistryAndServeHTTP(*metricsAddr)
defer zamknijSerwerMetryk()
// Pozostawiamy nazwę przestrzeni nazw i podsystem puste
konstruktorMetryk := metrics.NewPrometheusMetricsBuilder(rejestrPrometheus, "", "")
konstruktorMetryk.AddPrometheusRouterMetrics(router)
// ...
Przykładowa aplikacja
Aby zrozumieć, jak działa panel sterujący w praktyce, można odnieść się do przykładowej aplikacji metryk.
Postępuj zgodnie z instrukcjami w README przykładu, aby ją uruchomić i dodać źródło danych Prometheus do Grafany.
Panel sterowania Grafany
Przygotowaliśmy panel sterowania Grafany do wykorzystania z wymienioną implementacją metryk. Zapewnia podstawowe informacje o przepustowości, wskaźniku awaryjności oraz czasie publikacji/przetwarzania.
Jeśli chcesz wyświetlić ten panel lokalnie, możesz skorzystać z przykładowej aplikacji.
Aby uzyskać więcej informacji na temat metryk eksportowanych do systemu Prometheus, zobacz Eksportowane metryki.
Importowanie panelu sterowania
Aby zaimportować panel sterowania Grafany, wybierz Panel sterowania/Zarządzaj z lewego menu, a następnie kliknij +Import
.
Wprowadź adres URL panelu https://grafana.com/dashboards/9777 (lub tylko ID, 9777), a następnie kliknij Wczytaj.
Następnie wybierz źródło danych Prometheus używane do zbierania punktu końcowego /metrics
. Kliknij Importuj
i koniec!
Eksportowane metryki
Poniżej znajduje się lista wszystkich metryk zarejestrowanych w rejestrze Prometheus przez PrometheusMetricsBuilder
.
Więcej informacji na temat typów metryk Prometheus można znaleźć w dokumentacji Prometheus.
Obiekt | Metryka | Opis | Etykiety/Wartości |
---|---|---|---|
Subskrybent | subscriber_messages_received_total |
Licznik Prometheus. Zlicza liczbę wiadomości odebranych przez subskrybenta. | acked to "acked" lub "nacked". |
Jeśli subskrybent działa w obrębie manipulatora, ustaw handler_name ; w przeciwnym razie " |
|||
subscriber_name identyfikuje subskrybenta. Jeśli implementuje interfejs fmt.Stringer , jest to wynik String() ; w przeciwnym razie jest to package.structName . |
|||
Manipulator | handler_execution_time_seconds |
Histogram Prometheus. Rejestruje czas wykonania funkcji manipulatora zawiniętej przez pośrednika. | handler_name to nazwa manipulatora. |
success to "true" lub "false", w zależności od tego, czy zawinięta funkcja manipulatora zwraca błąd. |
|||
Wydawca | publish_time_seconds |
Histogram Prometheus. Rejestruje czas wykonania udekorowanej funkcji publikującej przez wydawcę. | success to "true" lub "false", w zależności od tego, czy udekorowany wydawca zwraca błąd. |
Jeśli wydawca działa w obrębie manipulatora, ustaw handler_name ; w przeciwnym razie " |
|||
publisher_name identyfikuje wydawcę. Jeśli implementuje interfejs fmt.Stringer , jest to wynik String() ; w przeciwnym razie jest to package.structName . |
Dodatkowo, każda metryka ma etykietę node
dostarczoną przez Prometheus, której wartość odpowiada instancji źródła metryki, oraz job
określone jako nazwa zadania w pliku konfiguracyjnym Prometheus.
Uwaga: Jak wspomniano powyżej, użycie niepustego namespace
lub subsystem
spowoduje prefixowanie nazwy metryki. Może być konieczne dostosowanie takie jak w definicji panelu na pulpicie nawigacyjnym Grafany.
Dostosowanie
Jeśli uważasz, że pewna metryka została pominięta, można łatwo rozszerzyć tę podstawową implementację. Najlepszym sposobem jest korzystanie z rejestru Prometheus używanego wraz z metodą ServeHTTP
oraz rejestrowanie metryk zgodnie z dokumentacją klienta Prometheus tutaj.
Zwięzły sposób aktualizacji tych metryk polega na wykorzystaniu dekoratorów. Pełen kod źródłowy można znaleźć pod adresem github.com/ThreeDotsLabs/watermill/message/decorator.go.
// ...
// MessageTransformSubscriberDecorator tworzy dekorator subskrybenta, który wywołuje funkcję transformacji dla każdej przekazywanej przez subskrybenta wiadomości.
func MessageTransformSubscriberDecorator(transform func(*Message)) SubscriberDecorator {
if transform == nil {
panic("funkcja transformacji jest pusta")
}
return func(sub Subscriber) (Subscriber, error) {
return &messageTransformSubscriberDecorator{
sub: sub,
transform: transform,
}, nil
}
}
// MessageTransformPublisherDecorator tworzy dekorator wydawcy, który wywołuje funkcję transformacji dla każdej przekazywanej przez wydawcę wiadomości.
func MessageTransformPublisherDecorator(transform func(*Message)) PublisherDecorator {
if transform == nil {
panic("funkcja transformacji jest pusta")
}
return func(pub Publisher) (Publisher, error) {
return &messageTransformPublisherDecorator{
Publisher: pub,
transform: transform,
}, nil
}
}
type messageTransformSubscriberDecorator struct {
// ...
I/lub używanie pośrednictwa w routerach.
Prostszym sposobem jest aktualizacja wymaganych metryk tylko w funkcjach manipulatora.