프로메테우스를 사용하여 워터밀의 실시간 모니터링
메트릭
워터밀은 발행자/구독자에 대한 데코레이터 및 핸들러에 대한 미들웨어를 사용하여 모니터링할 수 있습니다. 공식 고 프로메테우스 클라이언트를 이용한 기본 구현을 제공합니다.
components/metrics
패키지는 발행자, 구독자, 핸들러를 감싸 관련 프로메테우스 레지스트리를 업데이트하기 위한 편리한 함수를 제공하는 PrometheusMetricsBuilder
를 노출합니다:
전체 소스 코드: github.com/ThreeDotsLabs/watermill/components/metrics/builder.go
// ...
// The PrometheusMetricsBuilder provides methods for decorating publishers, subscribers, and handlers.
type PrometheusMetricsBuilder struct {
// PrometheusRegistry can fill in an existing Prometheus registry or be empty to use the default registry.
PrometheusRegistry prometheus.Registerer
Namespace string
Subsystem string
}
// AddPrometheusRouterMetrics는 메시지 라우터의 모든 핸들러에 메트릭 미들웨어를 추가하는 편리한 함수이며, 해당 핸들러의 발행자 및 구독자를 장식합니다.
func (b PrometheusMetricsBuilder) AddPrometheusRouterMetrics(r *message.Router) {
// ...
발행자, 구독자, 핸들러 래핑
대부분의 경우 권장되는 워터밀 라우터를 사용하는 경우, 이 라우터에 추가된 모든 핸들러가 프로메테우스 레지스트리를 업데이트하기 위해 감싸지도록 하며, 이러한 핸들러의 발행자와 구독자도 함께 감싸도록 하기 위해 편리한 함수인 AddPrometheusRouterMetrics
를 사용할 수 있습니다:
전체 소스 코드: 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)
// ...
위의 코드 스니펫에서는 namespace
와 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는 /metrics 엔드포인트를 노출하기 위해 주어진 주소에 HTTP 서버를 설정합니다.
// 이는 새로운 Prometheus 레지스트리(메트릭 등록을 위해)와 서버를 종료하기 위한 취소 함수를 반환합니다.
func CreateRegistryAndServeHTTP(addr string) (registry *prometheus.Registry, cancel func()) {
registry = prometheus.NewRegistry()
return registry, ServeHTTP(addr, registry)
}
// ServeHTTP는 /metrics 엔드포인트를 노출하기 위해 주어진 주소에 HTTP 서버를 설정합니다.
// 이는 기존의 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에 있는 지침을 따라 실행하고, Grafana에 Prometheus 데이터 소스를 추가하세요.
Grafana 대시보드
위에서 언급한 메트릭 구현과 함께 사용할 Grafana 대시보드가 준비되어 있습니다. 이는 처리량, 실패율, 및 게시/처리 기간에 대한 기본 정보를 제공합니다.
이 대시보드를 지역에서 보고 싶으면, 예시 어플리케이션을 사용할 수 있습니다.
Prometheus로 내보낸 메트릭에 대한 자세한 정보는 "내보낸 메트릭"을 참고하세요.
대시보드 가져오기
그래프나 대시보드를 가져오려면 왼쪽 메뉴에서 대시보드/관리를 선택한 후 +가져오기
를 클릭하세요.
대시보드 URL https://grafana.com/dashboards/9777 (또는 ID인 9777)를 입력한 후, 불러오기
를 클릭하세요.
그런 다음 /metrics
엔드포인트를 가져오기 위해 사용된 Prometheus 데이터 소스를 선택하세요. 가져오기
를 클릭하면 끝났습니다!
내보낸 메트릭
PrometheusMetricsBuilder
에 의해 Prometheus 레지스트리에 등록된 모든 메트릭을 나열합니다.
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 히스토그램. 미들웨어에 의해 감싸인 핸들러 함수의 실행 시간을 기록합니다. | handler_name 은 핸들러의 이름입니다. |
success 는 래핑된 핸들러 함수가 오류를 반환하는지에 따라 "true" 또는 "false"입니다. |
|||
Publisher | publish_time_seconds |
Prometheus 히스토그램. 발행자의 데코레이트된 발행 함수의 실행 시간을 기록합니다. | success 는 데코레이트된 발행자가 오류를 반환하는지에 따라 "true" 또는 "false"입니다. |
만약 발행자가 핸들러 내에서 작동하는 경우, handler_name 을 설정하십시오. 그렇지 않으면 " |
|||
publisher_name 은 발행자를 식별합니다. fmt.Stringer 인터페이스를 구현하는 경우 String() 의 결과가 됩니다. 그렇지 않으면 package.structName 이 됩니다. |
또한, 각 메트릭은 프로메테우스에 의해 제공되는 node
라벨을 가지고 있으며, 해당 값은 메트릭 소스의 인스턴스에 해당하며, job
은 프로메테우스 구성 파일에서의 작업 이름으로 지정됩니다.
참고: 앞에서 언급한대로, 비어 있지 않은 namespace
또는 subsystem
을 사용하면 메트릭 이름 접두사가 생깁니다. 따라서 Grafana 대시보드의 패널 정의와 같은 해당 조정이 필요할 수 있습니다.
사용자 정의
특정 메트릭이 누락된 것으로 판단된다면, 이 기본 구현을 쉽게 확장할 수 있습니다. 최상의 방법은 ServeHTTP
메서드를 사용하여 Prometheus 레지스트리를 사용하고, 여기의 Prometheus 클라이언트 문서에 따라 메트릭을 등록하는 것입니다.
이러한 메트릭을 업데이트하는 간결한 방법은 데코레이터를 사용하는 것입니다. 전체 소스 코드는 github.com/ThreeDotsLabs/watermill/message/decorator.go에서 찾을 수 있습니다.
// ...
// MessageTransformSubscriberDecorator는 구독자를 생성하는 데코레이터를 만들어서 구독자를 통해 전달된 각 메시지에 대해 transform 함수를 호출합니다.
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는 발행자를 생성하는 데코레이터를 만들어서 발행자를 통해 전달된 각 메시지에 대해 transform 함수를 호출합니다.
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 {
// ...
또는 라우터의 미들웨어를 사용할 수 있습니다.
또 다른 간단한 방법은 처리기 함수에서 필요한 메트릭들만 업데이트하는 것입니다.