نظارت به صورت زمان واقعی بر Watermill با استفاده از Prometheus

معیارها

Watermill می‌تواند با استفاده از دکوراتورها برای ارسال‌کنندگان/گیرند‌گان و میان افزارها برای دستگیرندگان نظارت شود. ما یک پیاده سازی پیش فرض با استفاده از کلاینت Prometheus برای Go ارائه می‌دهیم.

بسته components/metrics، PrometheusMetricsBuilder را صادر می‌کند که توابع مناسبی به منظور پیچیده‌سازی ارسال‌کنندگان، گیرندگان و دستگیرندگان برای به روزرسانی ثبت Prometheus مربوطه فراهم می‌کند:

کد منبع کامل: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// PrometheusMetricsBuilder متدهایی را برای دکوره‌ای ارسال‌کنندگان، گیرندگان و دستگیرندگان فراهم می‌کند.
type PrometheusMetricsBuilder struct {
    // PrometheusRegistry می‌تواند یک ثبت Prometheus موجود را پر کند یا خالی بماند تا از ثبت پیش فرض استفاده شود.
    PrometheusRegistry prometheus.Registerer

    فضای‌نام string
    زیرسیستم string
}

// AddPrometheusRouterMetrics یک تابع مناسب برای اضافه کردن میان افزارهای معیار به تمام دستگیرندگان روی مسیر پیام است. همچنین ارسال‌کنندگان و گیرندگان دستگیرندگان را پیچیده می‌کند.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
// ...

پیچیده‌سازی ارسال‌کنندگان، گیرندگان و دستگیرندگان

اگر از مسیری‌گر Watermill استفاده می‌کنید (که در بیشتر موارد توصیه می‌شود)، می‌توانید از تابع مناسب AddPrometheusRouterMetrics استفاده کنید تا اطمینان حاصل کنید که تمام دستگیرندگان اضافه شده به این مسیر به منظور به‌روزرسانی ثبت Prometheus پیچیده شوند، همچنین ارسال‌کنندگان و گیرندگان آن‌ها:

کد منبع کامل: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// AddPrometheusRouterMetrics یک تابع مناسب برای اضافه کردن میان افزارهای معیار به تمام دستگیرندگان روی مسیر پیام است. همچنین ارسال‌کنندگان و گیرندگان دستگیرندگان را پیچیده می‌کند.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
    r.AddPublisherDecorators(b.DecoratePublisher)
    r.AddSubscriberDecorators(b.DecorateSubscriber)
    r.AddMiddleware(b.NewRouterMiddleware().Middleware)
}
// ...

مثال استفاده از AddPrometheusRouterMetrics:

کد منبع کامل: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

// ...
    // ما فضای‌نام و زیرسیستم را خالی می‌گذاریم
    metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "")
    metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

در قطعه کد فوق، ما پارامترهای فضای‌نام و زیرسیستم را خالی می‌گذاریم. کتابخانه کلاینت Prometheus از این پارامترها استفاده می‌کند تا نام‌های معیار را پیشوند دهد. ممکن است بخواهید از یک فضای‌نام یا زیرسیستم استفاده کنید، اما لطفاً توجه داشته باشید که این تاثیری بر روی نام‌های معیار دارد، بنابراین شما باید داشبورد Grafana را متناسب با آن تنظیم کنید.

ارسال‌کنندگان و گیرندگان مستقل نیز می‌توانند با استفاده از متدهای اختصاصی PrometheusMetricsBuilder پیچیده شوند:

کد منبع کامل: 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)
    }
// ...

افشای نقطه پایانی /metrics

بر اساس اصل کاری پرومتئوس، یک سرویس باید یک نقطه پایانی HTTP برای جمع‌آوری داده‌ها افشا کند. به طور رایج، این یک نقطه پایانی GET است و مسیر معمولاً /metrics است.

برای ارائه این نقطه پایانی، دو تابع مناسب وجود دارد. یکی از آن‌ها از رجیستری پرومتئوس ایجاد شده از قبل استفاده می‌کند و دیگری به طور همزمان یک رجیستری جدید ایجاد می‌کند:

کد منبع کامل: github.com/ThreeDotsLabs/watermill/components/metrics/http.go

// ...
کد منبع کامل: [github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go](https://github.com/ThreeDotsLabs/watermill/tree/master/_examples/basic/4-metrics/main.go#L90)

```go
// ...
	prometheusRegistry, closeMetricsServer := metrics.CreateRegistryAndServeHTTP(*metricsAddr)
	defer closeMetricsServer()

	// ما فضای نام و زیرسیستم را خالی می‌گذاریم
	metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "")
	metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

نمونه برنامه

برای درک اینکه داشبورد در عمل چگونه کار می‌کند، می‌توانید به نمونه متریک مراجعه کنید.

دستورالعمل‌ها را در README نمونه دنبال کرده و منبع داده پرومتئوس را به گرافانا اضافه کنید.

داشبورد گرافانا

ما یک داشبورد گرافانا را برای استفاده با اجرای متریک‌های گفته شده آماده کرده‌ایم. این داشبورد اطلاعات اساسی درباره نرخ تردد، نرخ شکست و مدت زمان انتشار/پردازش را ارائه می‌دهد.

اگر می‌خواهید این داشبورد را به صورت محلی مشاهده کنید، می‌توانید از برنامه نمونه استفاده کنید.

برای کسب اطلاعات بیشتر در مورد متریک‌های صادر شده به پرومتئوس، به متریک‌های صادر شده مراجعه کنید.

وارد کردن داشبورد

برای وارد کردن داشبورد گرافانا، از منوی سمت چپ Dashboard/Manage را انتخاب کنید و سپس بر روی +Import کلیک کنید.

آدرس داشبورد https://grafana.com/dashboards/9777 (یا فقط شناسه 9777) را وارد کرده و روی Load کلیک کنید.

وارد کردن داشبورد

سپس منبع داده پرومتئوس مورد استفاده برای جمع‌آوری نقطه پایانی /metrics را انتخاب کنید. روی Import کلیک کرده و تمام شد!

معیارهای صادر شده

زیر موارد فهرست همه معیارهای ثبت شده در ثبت کننده Prometheus توسط PrometheusMetricsBuilder را نمایش می‌دهد.

برای کسب اطلاعات بیشتر در مورد انواع معیار Prometheus، لطفا به مستندات Prometheus مراجعه کنید.

شی معیار توضیحات برچسب‌ها/مقادیر
Subscriber subscriber_messages_received_total یک شمارنده Prometheus. تعداد پیام‌های دریافت شده توسط یک مشترک را می‌شمارد. acked برابر "acked" یا "nacked" است.
اگر مشترک در یک دستگیرنده (handler) فعالیت می‌کند، handler_name را تنظیم کنید. در غیر این صورت، "".
subscriber_name مشترک را شناسایی می‌کند. اگر رابط fmt.Stringer را پیاده‌سازی می‌کند، نتیجهString() است؛ در غیر این صورت، package.structName است.
Handler handler_execution_time_seconds یک توزیع Prometheus است. زمان اجرای تابع دستگیرنده (handler) که توسط میان‌افزار پیچیده شده را ثبت می‌کند. handler_name نام دستگیرنده است.
success برابر "true" یا "false" است، بسته به اینکه تابع دستگیرنده پیچیده شده خطایی را برمی‌گرداند یا خیر.
Publisher publish_time_seconds یک توزیع Prometheus است. زمان اجرای تابع publish تزئین شده از ناشر را ثبت می‌کند. success برابر "true" یا "false" است، بسته به اینکه توزئین شده ناشر خطایی را برگرداند یا خیر.
اگر ناشر در یک دستگیرنده فعالیت می‌کند، handler_name را تنظیم کنید. در غیر این صورت، "".
publisher_name ناشر را شناسایی می‌کند. اگر رابط fmt.Stringer را پیاده‌سازی می‌کند، نتیجه‌اش String() است؛ در غیر این صورت، package.structName است.

به علاوه، هر معیار دارای یک برچسبnode از سوی Prometheus دارد، که مقدار آن متناظر با نمونه منبع معیار است، و یک job که به عنوان نام کار در فایل پیکربندی Prometheus مشخص شده است.

توجه: همانطور که گفته شد، استفاده از namespace یا subsystem غیرخالی منجر به پیشوند نام معیار می‌شود. لذا ممکن است نیاز به تنظیمات متناظر مانند تعریف پنل در یک داشبورد Grafana داشته باشید.

سفارشی‌سازی

اگر معتقدید که یک معیار خاصی چشم‌پوشی شده است، می‌توانید به راحتی این پیاده‌سازی پایه را گسترش دهید. بهترین روش استفاده از ثبت کننده Prometheus استفاده شده با ServeHTTP است و معیارها را طبق مستندات مشتری Prometheus ثبت کنید.

یک روش مختصر برای به‌روزرسانی این معیارها استفاده از تزئین‌گرها است. کد منبع کامل را می‌توانید در github.com/ThreeDotsLabs/watermill/message/decorator.go بیابید.

// ...
// MessageTransformSubscriberDecorator یک تزئین‌گر مشترک ایجاد می‌کند که تابع تبدیل را روی هر پیامی که از طریق مشترک گذر می‌کند فرا می‌خواند.
func MessageTransformSubscriberDecorator(transform func(*Message)) SubscriberDecorator {
	if transform == nil {
		panic("تابع تبدیل نال است")
	}
	return func(sub Subscriber) (Subscriber, error) {
		return &messageTransformSubscriberDecorator{
			sub:       sub,
			transform: transform,
		}, nil
	}
}

// MessageTransformPublisherDecorator یک تزئین‌گر ناشر ایجاد می‌کند که تابع تبدیل را روی هر پیامی که از طریق ناشر گذر می‌کند فرا می‌خواند.
func MessageTransformPublisherDecorator(transform func(*Message)) PublisherDecorator {
	if transform == nil {
		panic("تابع تبدیل نال است")
	}
	return func(pub Publisher) (Publisher, error) {
		return &messageTransformPublisherDecorator{
			Publisher: pub,
			transform: transform,
		}, nil
	}
}

type messageTransformSubscriberDecorator struct {
// ...

و/یا از میان‌افزار در مسیریاب‌ها.

یک روش ساده‌تر این است که فقط در توابع دستگیرنده‌ها معیارهای مورد نیاز را به‌روزکنید.