การตรวจสอบแบบเรียลไทม์ของ Watermill โดยใช้ Prometheus

การเก็บค่าการวัด

Watermill สามารถตรวจสอบได้โดยใช้ตัวบอก (decorators) สำหรับผู้เผยแพร่/ผู้บริโภค และ middleware สำหรับตัวจัดการ (handlers) โดยเรามีการประยุกต์ใช้งานที่ค่าเริ่มต้น โดยใช้ Prometheus client for Go อย่างเป็นทางการ

แพคเกจ components/metrics ส่งออก PrometheusMetricsBuilder ซึ่งมีฟังก์ชันที่สะดวกสบายสำหรับการห่อผู้เผยแพร่, ผู้บริโภค, และตัวจัดการเพื่ออัพเดทรีจิสทรีของ Prometheus ที่เกี่ยวข้อง:

รหัสภายใน: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// PrometheusMetricsBuilder มีเมธอดสำหรับการตกแต่งผู้เผยแพร่, ผู้บริโภค, และตัวจัดการ
type PrometheusMetricsBuilder struct {
    // PrometheusRegistry สามารถเติมในรีจิสทรี Prometheus ที่มีอยู่หรือเป็นค่าว่างเพื่อใช้รีจิสทรีเริ่มต้น
    PrometheusRegistry prometheus.Registerer

    Namespace string
    Subsystem string
}

// เพิ่มเมตริกของ PrometheusRouterMetrics เป็นฟังก์ชันที่สะดวกสบายสำหรับเพิ่ม middleware เมตริกไปยังตัวจัดการทุกตัวในเส้นทางของข้อความ ทั้งนี้ยังตกแต่งผู้เผยแพร่และผู้บริโภคของตัวจัดการด้วย
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
// ...

การห่อผู้เผยแพร่, ผู้บริโภค, และตัวจัดการ

หากคุณใช้ตัวจัดการของ Watermill (ซึ่งเราขอแนะนำให้ใช้ในกรณีส่วนมาก) คุณสามารถใช้ฟังก์ชันที่สะดวกสบาย AddPrometheusRouterMetrics เพื่อให้แน่ใจว่าทุกตัวจัดการที่เพิ่มเข้าไปในตัวจัดการนี้ได้รับการห่อเพื่ออัพเดทรีจิสทรีของ Prometheus, อีกทั้งยังรวมถึงผู้เผยแพร่และผู้บริโภคของพวกเขา:

รหัสภายใน: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go

// ...
// AddPrometheusRouterMetrics เป็นฟังก์ชันที่สะดวกสบายสำหรับเพิ่ม middleware เมตริกไปยังตัวจัดการทุกตัวในเส้นทางของข้อความ ทั้งนี้ยังตกแต่งผู้เผยแพร่และผู้บริโภคของตัวจัดการด้วย
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 และ subsytem ว่างเปล่า
    metricsBuilder := metrics.NewPrometheusMetricsBuilder(prometheusRegistry, "", "")
    metricsBuilder.AddPrometheusRouterMetrics(router)
// ...

ในตัวอย่างของรหัสด้านบน, เราปล่อยให้พารามิเตอร์ namespace และ subsystem ว่างเปล่า. ไลบรารีไคลเอ็นต์ของ Prometheus ใช้พารามิเตอร์เหล่านี้ เพื่อใส่คำนำหน้าในชื่อข้อความเมตริก คุณอาจต้องการใช้ namespace หรือ subsytem แต่โปรดทราบว่านี้จะส่งผลต่อชื่อเมตริก ดังนั้นคุณจำเป็นต้องปรับแต่งแดชบอร์ด 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 Endpoint

ตามการทำงานของ Prometheus บริการจำเป็นต้องเปิดเผยจุดปลายทาง HTTP สำหรับการดึงข้อมูล ตามปกตินี้คือจุดปลายทางที่ใช้วิธี GET และเส้นทางปกติคือ /metrics

เพื่อให้มีจุดปลายทางนี้สามารถให้บริการได้มีฟังก์ชันสะดวก 2 ฟังก์ชันที่ใช้เพิกถอนทะเบียน Prometheus ที่สร้างไว้ก่อนหน้านี้และอีกฟังก์ชันหนึ่งสร้างทะเบียนใหม่พร้อมกัน:

โค้ดที่เต็มรูปแบบ: github.com/ThreeDotsLabs/watermill/components/metrics/http.go

// ...
// สร้างทะเบียนและให้บริการเปิดเผย 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)
// ...

ตัวอย่างแอปพลิเคชัน

เพื่อเข้าใจว่าแดชบอร์ดทำงานอย่างไรในแง่ปฏิบัติ คุณสามารถอ้างอิงไปยัง ตัวอย่าง metrics

ทำตามคำแนะนำใน README ของตัวอย่างเพื่อรันและเพิ่มต้นทางข้อมูลของ Prometheus เข้าไปใน Grafana

แดชบอร์ด Grafana

เราได้เตรียมไว้แล้ว แดชบอร์ด Grafana เพื่อใช้งานร่วมกับการปฏิบัติที่กล่าวถึง มันจะให้ข้อมูลพื้นฐานเกี่ยวกับผลผลิต, อัตราการล้มเหลว และระยะเวลาในการเผยแพร่/ประมวลผล

หากคุณต้องการมุ่งเห็นแดชบอร์ดนี้ในท้องถิ่น คุณสามารถใช้แอปพลิเคชันตัวอย่างได้

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการส่งออกข้อมูลเมตริกไปยัง Prometheus ดูที่ Exported metrics

การนำเข้าแดชบอร์ด

เพื่อนำเข้าแดชบอร์ด Grafana, เลือก แดชบอร์ด/จัดการ จากเมนูด้านซ้าย จากนั้นคลิก + นำเข้า

ป้อน URL ของแดชบอร์ด https://grafana.com/dashboards/9777 (หรือเพียงแค่ ID, 9777), จากนั้นคลิก โหลด

การนำเข้าแดชบอร์ด

จากนั้นเลือกต้นทางข้อมูล Prometheus ที่ใช้สแกนจุดปลายทาง /metrics คลิก นำเข้า และเสร็จสิ้น!

การส่ง Metrics

ต่อไปนี้คือรายการของ metrics ที่ลงทะเบียนในทะเบียน Prometheus โดย PrometheusMetricsBuilder

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับประเภทของ metrics ของ Prometheus โปรดอ่านที่ เอกสารของ Prometheus.

วัตถุ Metric คำอธิบาย ป้าย/ค่า
Subscriber subscriber_messages_received_total ตัวนับของ Prometheus นับจำนวนข้อความที่ได้รับจาก Subscriber acked เป็น "acked" หรือ "nacked"
หาก Subscriber ทำงานภายใน handler ให้ตั้ง handler_name; หากไม่ใช่ ให้ใช้ ""
subscriber_name ระบุ Subscriber ถ้ามันได้ implement interface fmt.Stringer นั้นคือผลลัพธ์จาก String(); ไม่ใช่ คือ package.structName

เรื่องเพิ่มเติม แต่ละ metric มี node label ที่ Prometheus จัดหา โดยมีค่าที่สอดคล้องกับตัวอย่างของตัวอย่าง source และ job ที่ระบุเป็นชื่องานใน ไฟล์การกำหนดค่าของ Prometheus.

หมายเหตุ: อ้างอิงจากที่กล่าวด้านบน การใช้ namespace หรือ subsystem ที่ไม่ว่างเปล่าจะทำให้เกิด prefix ของชื่อ metric คุณอาจจำเป็นต้องทำการปรับปรุงที่สอดคล้องเช่นการกำหนดค่าพาเนลในแดชบอร์ดของ Grafana

การปรับแต่ง

หากคุณคิดว่ามี metrics บางประการที่ของการตีคลุมคุณสามารถขยายบทบาทนี้ได้อย่างง่ายดาย วิธีที่ดีที่สุดคือการใช้การลงทะเบียน Prometheus ที่ใช้กับวิธี ServeHTTP และลงทะเบียน metrics ตามเอกสารของ client ที่นี่.

วิธีย่อที่สุดในการปรับปรุง metrics เหล่านี้คือโดยใช้ decorators โค้ดเต็มอื่น ๆ สามารถค้นหาได้ที่ github.com/ThreeDotsLabs/watermill/message/decorator.go.

// ...
// MessageTransformSubscriberDecorator สร้าง subscriber decorator ที่เรียกฟังก์ชัน transform บนทุกข้อความที่ถ่ายทออกผ่าน subscriber
func MessageTransformSubscriberDecorator(transform func(*Message)) SubscriberDecorator {
	if transform == nil {
		panic("transform function is nil")
	}
	return func(sub Subscriber) (Subscriber, error) {
		return &messageTransformSubscriberDecorator{
			sub:       sub,
			transform: transform,
		}, nil
	}
}

// MessageTransformPublisherDecorator สร้าง publisher decorator ที่เรียกฟังก์ชัน transform บนทุกข้อความที่ถ่ายทออกผ่าน publisher
func MessageTransformPublisherDecorator(transform func(*Message)) PublisherDecorator {
	if transform == nil {
		panic("transform function is nil")
	}
	return func(pub Publisher) (Publisher, error) {
		return &messageTransformPublisherDecorator{
			Publisher: pub,
			transform: transform,
		}, nil
	}
}

type messageTransformSubscriberDecorator struct {
// ...

และ/หรือ middleware ใน routers

วิธีที่ง่ายที่สุดคือการปรับปรุง metrics ที่จำเป็นเท่านั้นในฟังก์ชันของ handler