المراقبة في الوقت الحقيقي لـ 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
    Subsystem 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

// ...
    // نترك معلمات namespace وsubsystem فارغة
    metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "")
    metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

في مقتطف الشيفرة أعلاه، نترك معلمات namespace وsubsystem فارغة. مكتبة عميل Prometheus تستخدم هذه المعلمات لإضافة بادئة لأسماء المقاييس. قد ترغب في استخدام الفضاء الاسم أو subsystem، ولكن يرجى ملاحظة أن هذا سيؤثر على أسماء المقاييس، لذا عليك ضبط لوحة القيادة 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

وفقًا لمبدأ عمل Prometheus ، يحتاج الخدمة إلى تعريض نقطة نهاية HTTP لجمع البيانات. عادةً ما تكون هذه نقطة نهاية GET ، والمسار عادةً ما يكون /metrics.

لتوفير هذه النقطة النهاية ، هناك وظيفتان ملائمتان متاحتان ، إحداهما باستخدام سجل Prometheus الذي تم إنشاؤه مسبقًا والأخرى بإنشاء سجل جديد في نفس الوقت:

الكود المصدري الكامل: github.com/ThreeDotsLabs/watermill/components/metrics/http.go

// ...
// CreateRegistryAndServeHTTP ينشئ خادم HTTP في العنوان المعطى لتعريض نقطة النهاية /metrics لـ Prometheus.
// يُرجع سجل Prometheus جديد (لتسجيل المقاييس) ووظيفة إلغاء لإيقاف تشغيل الخادم.
func CreateRegistryAndServeHTTP(addr string) (registry *prometheus.Registry, cancel func()) {
	registry = prometheus.NewRegistry()
	return registry, ServeHTTP(addr, registry)
}

// ServeHTTP يوضح خادم HTTP في العنوان المعطى لتعريض نقطة النهاية /metrics لـ Prometheus.
// يقبل سجل Prometheus الحالي ويُرجع وظيفة إلغاء لإيقاف تشغيل الخادم.
func ServeHTTP(addr string, registry *prometheus.Registry) (cancel func()) {
// ...

وفيما يلي مثال على الاستخدام:

الكود المصدري الكامل: github.com/ThreeDotsLabs/watermill/_examples/basic/4-metrics/main.go

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

	// نترك المساحة الاسمية والنظام الفرعي فارغة
	metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "")
	metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

تطبيق مثال

لفهم كيفية عمل لوحة المعلومات عمليًا ، يمكنك الرجوع إلى مثال المقاييس.

اتبع التعليمات في README لتشغيلها وإضافة مصدر بيانات Prometheus إلى Grafana.

لوحة المعلومات في Grafana

لقد قمنا بتحضير لوحة معلومات Grafana لاستخدامها مع تنفيذ المقاييس المذكورة. تقدم معلومات أساسية عن معدل الإنتاجية ومعدل الفشل ومدة النشر/المعالجة.

إذا كنت ترغب في عرض هذه اللوحة المعلومات محليًا ، يمكنك استخدام تطبيق المثال.

لمزيد من المعلومات حول المقاييس المصدرة لـ Prometheus ، راجع المقاييس المصدرة.

استيراد اللوحة المعلومات

ليتمكن من استيراد لوحة المعلومات في Grafana ، حدد Dashboard/Manage من القائمة اليسرى ، ثم انقر على +Import.

أدخل عنوان URL لوحة المعلومات https://grafana.com/dashboards/9777 (أو فقط الرقم التعريفي، 9777) ، ثم انقر على Load.

استيراد اللوحة المعلومات

ثم حدد مصدر البيانات Prometheus المستخدم لجمع نقطة النهاية /metrics. انقر فوق Import وانتهى الأمر!

المقاييس المصدرة

يُدرج أدناه جميع المقاييس المسجلة في سجل Prometheus بواسطة "PrometheusMetricsBuilder".

للحصول على مزيد من المعلومات حول أنواع المقاييس في Prometheus، يرجى الرجوع إلى وثائق Prometheus.

الكائن المقياس الوصف العلامات/القيم
Subscriber subscriber_messages_received_total عداد Prometheus. يحسب عدد الرسائل التي تم استقبالها بواسطة المشترك. acked هو "acked" أو "nacked".
إذا كان المشترك يعمل داخل معالج، ضع handler_name؛ وإلا، "".
subscriber_name يعرف المشترك. إذا قام بتنفيذ واجهة fmt.Stringer، فإنه ناتج String()؛ وإلا، فهو package.structName.
Handler handler_execution_time_seconds هيستوغرام Prometheus. يسجل وقت تنفيذ دالة المعالج المغلفة بواسطة "middleware". handler_name هو اسم المعالج.
success هو "true" أو "false"، حسب ما إذا كانت دالة المعالج المغلفة تعيد خطأ أم لا.
Publisher publish_time_seconds هيستوغرام Prometheus. يسجل وقت تنفيذ دالة النشر المُزينة للناشر. 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 {
// ...

و/أو باستخدام middleware في الموجهات.

الطريقة الأبسط هي تحديث المقاييس المطلوبة فقط في دوال المعالج.