PrometheusListener¶
PrometheusListener는 Circuit Breaker의 상태와 전환을 Prometheus 서버에서 스크랩할 수 있는 메트릭으로 내보냅니다. 이를 통해 대시보드를 구축하고, 알림을 생성하며, 시간이 지남에 따라 서비스의 안정성에 대한 깊은 통찰력을 얻을 수 있습니다.
설치¶
이 Listener는 prometheus-client 라이브러리가 필요합니다. 다음 명령어로 추가 설치할 수 있습니다.
pip install fluxgate[prometheus]
노출되는 메트릭¶
두 가지 핵심 메트릭을 내보냅니다.
circuit_breaker_state (Gauge)¶
이 메트릭은 Circuit Breaker의 현재 상태를 나타냅니다. Gauge이므로 값이 오르거나 내릴 수 있습니다.
라벨:
circuit_name: Circuit Breaker의 이름.state: 상태의 이름 (closed,open,half_open등).
값:
1: Circuit이 현재 이 상태에 있습니다.0: Circuit이 이 상태에 있지 않습니다.
PromQL 사용 예시:
-
모든 열린 Circuit 찾기:
circuit_breaker_state{state="open"} == 1 -
각 상태에 있는 Circuit의 수 계산:
sum(circuit_breaker_state) by (state)
circuit_breaker_state_transition_total (Counter)¶
이 메트릭은 Circuit Breaker가 한 상태에서 다른 상태로 전환된 총 횟수를 계산합니다. Counter이므로 값은 항상 증가하기만 합니다.
라벨:
circuit_name: Circuit Breaker의 이름.old_state: Circuit이 전환된 이전 상태.new_state: Circuit이 전환된 새로운 상태.
PromQL 사용 예시:
-
지난 5분 동안 Circuit이 열린 비율 계산:
sum(rate(circuit_breaker_state_transition_total{new_state="open"}[5m])) by (circuit_name) -
지난 1시간 동안
HALF_OPEN상태로의 모든 전환 횟수 계산:increase(circuit_breaker_state_transition_total{new_state="half_open"}[1h])
사용법¶
기본 설정¶
간단한 스크립트나 백그라운드 워커의 경우, Prometheus 클라이언트의 HTTP 서버를 별도의 스레드에서 시작할 수 있습니다.
from prometheus_client import start_http_server
from fluxgate import CircuitBreaker
from fluxgate.listeners.prometheus import PrometheusListener
# 포트 8000에서 Prometheus 메트릭 서버를 시작합니다.
# 이 서버는 백그라운드 스레드에서 실행되며 블로킹되지 않습니다.
start_http_server(8000)
cb = CircuitBreaker(
name="payment_api",
...,
listeners=[PrometheusListener()],
)
# 여기에 애플리케이션 로직을 작성...
# 메트릭은 http://localhost:8000/metrics 에서 확인 가능합니다.
웹 프레임워크와의 통합 (FastAPI)¶
FastAPI 또는 Flask와 같은 웹 프레임워크를 사용할 때는 별도의 서버를 시작하는 대신 Prometheus 메트릭 엔드포인트를 애플리케이션에 직접 통합해야 합니다.
from fastapi import FastAPI
from prometheus_client import make_asgi_app
from fluxgate import AsyncCircuitBreaker
from fluxgate.listeners.prometheus import PrometheusListener
# Prometheus 메트릭을 위한 ASGI 앱을 생성합니다.
metrics_app = make_asgi_app()
app = FastAPI()
# /metrics 엔드포인트에 메트릭 앱을 마운트합니다.
app.mount("/metrics", metrics_app)
cb = AsyncCircuitBreaker(
name="api_gateway",
...,
listeners=[PrometheusListener()],
)
@app.get("/")
@cb
async def root():
# 보호된 API 로직...
return {"message": "Hello World"}
참고:
prometheus-client라이브러리는 스레드 안전하므로, 단일PrometheusListener인스턴스를 동기 및 비동기 Circuit Breaker 모두에서 안전하게 사용할 수 있습니다. 자세한 내용은 공식 문서를 참조하십시오.
여러 Circuit Breaker 모니터링¶
동일한 애플리케이션에서 여러 Circuit Breaker를 모니터링하려면, 동일한 PrometheusListener 인스턴스를 재사용하기만 하면 됩니다. 각 브레이커에 대해 메트릭은 circuit_name으로 올바르게 라벨링됩니다.
from prometheus_client import start_http_server
from fluxgate import CircuitBreaker
from fluxgate.listeners.prometheus import PrometheusListener
start_http_server(8000)
# 단일 Listener 인스턴스를 생성합니다.
listener = PrometheusListener()
# 여러 브레이커에 동일한 인스턴스를 추가합니다.
payment_cb = CircuitBreaker(name="payment_api", ..., listeners=[listener])
inventory_cb = CircuitBreaker(name="inventory_api", ..., listeners=[listener])
Grafana 대시보드 예시¶
다음은 Grafana 대시보드에서 이러한 메트릭을 시각화하는 방법의 몇 가지 예시입니다.
패널: "현재 열린 Circuit" (Stat)¶
- 쿼리:
sum(circuit_breaker_state{state="open"}) - 시각화: Stat
- 단위: 없음
- 임계값: 기본: 0, 단계 1: 1 (경고), 단계 2: 5 (치명적)
- 설명: 현재
OPEN상태에 있는 모든 Circuit Breaker의 실시간 수를 보여줍니다.
패널: "Circuit 트립률 (5분)" (Time Series)¶
- 쿼리:
sum(rate(circuit_breaker_state_transition_total{new_state="open"}[5m])) by (circuit_name) - 시각화: 시계열
- 단위: 초당 전환 수
- 범례:
{{circuit_name}} - 설명: 지난 5분 동안 평균화된, Circuit이 열린 초당 트립률을 보여줍니다. 추세를 파악하고 문제 있는 서비스를 식별하는 데 유용합니다.
패널: "Circuit 상태 개요" (Pie Chart)¶
- 쿼리:
sum(circuit_breaker_state) by (state) - 시각화: 파이 차트
- 단위: 없음
- 값 옵션:
모든 값 - 설명: 전체 시스템에 걸쳐 Circuit Breaker 상태 분포에 대한 높은 수준의 개요를 제공합니다.
커스텀 메트릭¶
추가적인 커스텀 메트릭을 내보내야 하는 경우, IListener 인터페이스를 구현하여 자체 Listener를 만들 수 있습니다.
from prometheus_client import Counter
from fluxgate.interfaces import IListener
from fluxgate.signal import Signal
from fluxgate.state import StateEnum
# OPEN 전환만 계산하는 커스텀 메트릭을 정의합니다.
OPEN_TRANSITIONS = Counter(
'circuit_breaker_open_total',
'Circuit Breaker가 열린 총 횟수',
['circuit_name']
)
class CustomPrometheusListener(IListener):
def __call__(self, signal: Signal) -> None:
if signal.new_state == StateEnum.OPEN:
OPEN_TRANSITIONS.labels(circuit_name=signal.circuit_name).inc()
다음 단계¶
- SlackListener: 상태 변경에 대한 실시간 알림을 받습니다.
- LogListener: 전환에 대한 상세 로깅을 구성합니다.
- Listener 개요: 메인 Listener 페이지로 돌아갑니다.