القابلية للرصد
القابلية للرصد هي عيون عمليات الإنتاج—المقاييس تكشف الاتجاهات، والسجلات تحدد الأسباب، وبيانات التتبع تحدد الأسباب الجذرية. هذه الأعمدة الثلاثة لا غنى عنها جميعًا.
1. ما ستتعلمه
- جمع مقاييس Micrometer: Counter / Gauge / Timer / DistributionSummary
- تهيئة جمع Prometheus وقواعد تنبيه استعلام PromQL
- لوحات Grafana المرئية: لوحات مقاييس JVM / HTTP / قاعدة البيانات
- التتبع الموزع OpenTelemetry وانتشار Span/Context
- Bob أنشأ لوحة SLO لـ OrderFlow: P99 < 100 مللي ثانية / معدل الخطأ < 0.1% / التوفر > 99.9%
2. قصة حقيقية لمهندس SRE
(1) نقطة الألم: استكشاف الأخطاء في الصندوق الأسود عبر الإنترنت
عندما تواجه OrderFlow خطأ في الإنتاج، يبقى Bob يبحث في بحر من سجلات التطبيق: أي واجهة برمجة تطبيقات بطيئة؟ أي خدمة بها مشاكل؟ أي خدمات مصغرة مرّ بها الطلب؟ ليس لديه أي فكرة. يقضي Alice وBob غالبًا أربع ساعات في استكشاف مشكلة إنتاج واحدة، ثلاث منها في "تخمين" مكان المشكلة.
(2) مقاربات الأعمدة الثلاثة للقابلية للرصد
graph LR
A["القابلية للرصد<br/>ثلاثة أعمدة"] --> B["المقاييس<br/>ماذا حدث؟<br/>Prometheus"]
A --> C["السجلات<br/>لماذا حدث؟<br/>ELK / Loki"]
A --> D["التتبع<br/>أين حدث؟<br/>Jaeger / Zipkin"]
حدد المشاكل باستخدام المقاييس، وحلل السجلات لتحديد السبب، واستخدم تحليل التتبع لتحديد السبب الجذري.
(3) الإيرادات
بعد أن أنشأ Bob نظام القابلية للرصد، بدأت لوحات Grafana تعرض زمن استجابة P99 ومعدلات الخطأ في الوقت الفعلي، وأطلقت تنبيهات Prometheus إشعارات تلقائية، وحددت بيانات تتبع OpenTelemetry السبب الجذري للمشاكل في غضون 30 ثانية. انخفض متوسط وقت حل المشاكل من 4 ساعات إلى 15 دقيقة.
3. جمع بيانات Micrometer
(1) أربعة أنواع من المقاييس
| النوع | المعنى | يزداد فقط | السيناريوهات النموذجية |
|---|---|---|---|
| Counter | عداد (زيادة فقط) | نعم | إجمالي الطلبات، عدد الطلبات المنشأة |
| Gauge | القيمة الحالية (يمكن أن تزداد أو تنقص) | لا | عدد الاتصالات الحالي، طول قائمة الانتظار |
| Timer | توزيع المدة | لا | وقت الاستجابة للطلب |
| DistributionSummary | إحصائيات التوزيع | لا | توزيع حجم نص الطلب |
(1) ▶ مثال: مقاييس أعمال مخصصة
@Service
public class OrderMetrics {
private final Counter orderCreatedCounter;
private final Counter orderCancelledCounter;
private final Timer orderCreationTimer;
private final Gauge pendingOrdersGauge;
public OrderMetrics(MeterRegistry registry, OrderRepository orderRepo) {
this.orderCreatedCounter = Counter.builder("orderflow.orders.created")
.description("إجمالي الطلبات المنشأة")
.tag("service", "orderflow")
.register(registry);
this.orderCancelledCounter = Counter.builder("orderflow.orders.cancelled")
.description("إجمالي الطلبات الملغاة")
.register(registry);
this.orderCreationTimer = Timer.builder("orderflow.orders.creation.duration")
.description("مدة إنشاء الطلب")
.publishPercentiles(0.5, 0.95, 0.99)
.publishPercentileHistogram()
.register(registry);
this.pendingOrdersGauge = Gauge.builder("orderflow.orders.pending",
orderRepo, repo -> repo.countByStatus("PENDING"))
.description("عدد الطلبات المعلقة الحالي")
.register(registry);
}
public void recordOrderCreated() {
orderCreatedCounter.increment();
}
public Timer.Sample startCreationTimer() {
return Timer.start(orderCreationTimer);
}
public void recordCreationComplete(Timer.Sample sample) {
sample.stop(orderCreationTimer);
}
}
الناتج:
// التنفيذ ناجح
(2) ▶ مثال: استخدام المقاييس في خدمة
@Service
public class OrderServiceImpl implements OrderService {
private final OrderMetrics metrics;
@Override
@Transactional
public Order createOrder(CreateOrderRequest request) {
Timer.Sample sample = metrics.startCreationTimer();
try {
Order order = doCreateOrder(request);
metrics.recordOrderCreated();
return order;
} finally {
metrics.recordCreationComplete(sample);
}
}
}
الناتج:
// التنفيذ ناجح
4. تكامل Prometheus
(1) ▶ مثال: تبعيات وتهيئة Prometheus
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
الناتج:
// التنفيذ ناجح
# application.yml
management:
endpoints:
web:
exposure:
include: health,prometheus,metrics
metrics:
tags:
application: ${spring.application.name}
export:
prometheus:
enabled: true
(2) ▶ مثال: تهيئة جمع Prometheus
# prometheus.yml
scrape_configs:
- job_name: 'orderflow'
metrics_path: '/actuator/prometheus'
scrape_interval: 15s
static_configs:
- targets: ['orderflow:8080']
الناتج:
تم تحميل تهيئة المراقبة
أهداف Prometheus: 3 نشطة
لوحة Grafana: جاهزة
(3) ▶ مثال: استعلامات PromQL والتنبيهات
# استعلامات PromQL
# زمن استجابة P99 لواجهة برمجة طلبات الطلبات
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{uri=~"/api/v1/orders.*"}[5m])) by (le, uri))
# معدل الخطأ (استجابات 5xx)
sum(rate(http_server_requests_seconds_total{status=~"5.."}[5m]))
/
sum(rate(http_server_requests_seconds_total[5m]))
# طلبات في الدقيقة
rate(orderflow_orders_created_total[1m]) * 60
الناتج:
تم تطبيق التهيئة بنجاح
# alert_rules.yml
groups:
- name: orderflow
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_server_requests_seconds_total{status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_total[5m])) > 0.001
for: 5m
labels:
severity: critical
annotations:
summary: "معدل خطأ OrderFlow يتجاوز 0.1%"
- alert: HighP99Latency
expr: |
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le)) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "زمن استجابة P99 لـ OrderFlow يتجاوز 100 مللي ثانية"
5. لوحة Grafana
(1) مقاييس لوحة المعلومات الرئيسية
| اللوحة | المقياس | PromQL |
|---|---|---|
| معدل الطلبات | QPS | sum(rate(http_server_requests_seconds_total[5m])) |
| زمن الاستجابة P50/P95/P99 | توزيع وقت الاستجابة | histogram_quantile(0.99, ...) |
| معدل الخطأ | نسبة 5xx | rate(...{status=~"5.."})/rate(...) |
| ذاكرة كومة JVM | استخدام الذاكرة | jvm_memory_used_bytes{area="heap"} |
| توقف GC | مدة GC | rate(jvm_gc_pause_seconds_sum[5m]) |
| اتصالات HikariCP النشطة | استخدام تجمع الاتصالات | hikaricp_connections_active |
| معدل إنشاء الطلبات | مقياس أعمال | rate(orderflow_orders_created_total[1m]) |
(1) ▶ مثال: مقتطف JSON للوحة Grafana
{
"dashboard": {
"title": "OrderFlow Observability",
"panels": [
{
"title": "معدل الطلبات (QPS)",
"type": "timeseries",
"targets": [{
"expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
}]
},
{
"title": "زمن استجابة P99",
"type": "timeseries",
"targets": [{
"expr": "histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{application=\"orderflow-service\"}[5m])) by (le))"
}]
},
{
"title": "معدل الخطأ",
"type": "gauge",
"targets": [{
"expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\",status=~\"5..\"}[5m])) / sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
}],
"fieldConfig": {
"defaults": {
"thresholds": {
"steps": [
{"value": 0, "color": "green"},
{"value": 0.001, "color": "red"}
]
}
}
}
}
]
}
}
الناتج:
{
"dashboard": {
"title": "OrderFlow Observability",
"panels": [
{
"title": "معدل الطلبات (QPS)",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(http_server_requests_seconds_total{application=\"orderflow-service\"}[5m]))"
}
]
},
{
"title": "زمن استجابة P99",
"type": "timeseries",
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(http_server_request
6. تتبع OpenTelemetry
(1) مفهوم التتبع الموزع
graph LR
A["العميل"] --> B["بوابة API<br/>Trace: abc123<br/>Span 1"]
B --> C["خدمة الطلبات<br/>Span 2<br/>parent: Span 1"]
C --> D["خدمة المنتجات<br/>Span 3<br/>parent: Span 2"]
C --> E["قاعدة البيانات<br/>Span 4<br/>parent: Span 2"]
| المفهوم | المعنى |
|---|---|
| Trace | المسار الكامل لطلب واحد |
| Span | عملية واحدة في السلسلة |
| Context | سياق التتبع المنقول بين Span |
| SpanId | المعرف الفريد للـ span الحالي |
| TraceId | المعرف الفريد للتتبع بالكامل |
(1) ▶ مثال: تبعية OpenTelemetry
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
الناتج:
// التنفيذ ناجح
# application.yml
otel:
exporter:
otlp:
endpoint: http://otel-collector:4317
resource:
attributes:
service.name: orderflow-service
traces:
exporter: otlp
(2) ▶ مثال: Span مخصص
@Service
public class OrderService {
private final Tracer tracer;
public OrderService(Tracer tracer) {
this.tracer = tracer;
}
public Order createOrder(CreateOrderRequest request) {
Span span = tracer.spanBuilder("create-order")
.setAttribute("product.id", request.productId())
.setAttribute("quantity", request.quantity())
.startSpan();
try (Scope scope = span.makeCurrent()) {
Order order = doCreateOrder(request);
span.setAttribute("order.id", order.getId());
return order;
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getMessage());
throw e;
} finally {
span.end();
}
}
}
الناتج:
// التنفيذ ناجح
7. مثال شامل: لوحة SLO لـ OrderFlow
# docker-compose.observability.yml
version: "3.9"
services:
prometheus:
image: prom/prometheus:latest
ports: ["9090:9090"]
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./alert_rules.yml:/etc/prometheus/alert_rules.yml
grafana:
image: grafana/grafana:latest
ports: ["3000:3000"]
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
- grafana-data:/var/lib/grafana
otel-collector:
image: otel/opentelemetry-collector:latest
ports: ["4317:4317", "4318:4318"]
volumes:
- ./otel-collector-config.yml:/etc/otelcol/config.yaml
jaeger:
image: jaegertracing/all-in-one:latest
ports: ["16686:16686"]
app:
build: .
ports: ["8080:8080"]
environment:
OTEL_EXPORTER_OTLP_ENDPOINT: http://otel-collector:4317
OTEL_SERVICE_NAME: orderflow-service
MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: health,prometheus,metrics
| مقياس SLO | الهدف | عتبة التنبيه | PromQL |
|---|---|---|---|
| زمن استجابة P99 | < 100 مللي ثانية | > 100 مللي ثانية لمدة 5 دقائق | histogram_quantile(0.99, ...) |
| معدل الخطأ | < 0.1% | > 0.1% لمدة 5 دقائق | rate(5xx)/rate(all) |
| التوفر | > 99.9% | < 99.9% لمدة 5 دقائق | 1 - rate(5xx)/rate(all) |
| إنتاجية الطلبات | > 1,000/دقيقة | < 500/دقيقة لمدة 10 دقائق | rate(orders_created)[1m]*60 |
❓ أسئلة شائعة
publishPercentiles وhistogram_quantile؟publishPercentiles يحسب النسب المئوية على جانب التطبيق (يوفر تخزين Prometheus، لكنه أقل دقة عبر مثيلات متعددة). histogram_quantile يحسب النسب المئوية على جانب Prometheus (يدعم تجميع المثيلات المتعددة وأكثر دقة). نوصي بالحساب على جانب Prometheus في بيئات الإنتاج.📖 ملخص
- أربعة مقاييس Micrometer: Counter، Gauge (القيمة الحالية)، Timer (المدة المنقضية)، DistributionSummary (التوزيع)
- Prometheus يجمع
/actuator/prometheus، استعلامات PromQL + قواعد التنبيه - لوحة Grafana تعرض مقاييس HTTP وJVM وتجمع الاتصالات والأعمال
- OpenTelemetry يجمع بيانات التتبع الموزع؛ Jaeger يخزنها ويعرضها
- لوحة SLO: P99 < 100 مللي ثانية / معدل الخطأ < 0.1% / التوفر > 99.9%
- تعاون الأعمدة الثلاثة: تحديد المشاكل عبر المقاييس ← تحديد الموقع عبر التتبع ← تحليل السجلات لتحديد السبب الجذري
📝 تمارين
-
تمرين أساسي (الصعوبة: ⭐): هيئ Micrometer + Prometheus لـ OrderFlow، واكشف نقطة النهاية
/actuator/prometheus، وحدد ثلاثة مقاييس أعمال مخصصة (عدد الطلبات المنشأة، وقت معالجة الطلبات، عدد الطلبات المعلقة). -
تمرين متقدم (الصعوبة: ⭐⭐): هيئ Prometheus لجمع البيانات وأعد لوحة Grafana لعرض HTTP QPS وزمن استجابة P99 ومعدل الخطأ وذاكرة كومة JVM. اكتب قاعدتي تنبيه Prometheus (لمعدل الخطأ العالي وزمن الاستجابة العالي).
-
تحدٍ (الصعوبة: ⭐⭐⭐): تكامل OpenTelemetry وJaeger لتنفيذ التتبع الموزع. أضف span مخصصًا إلى OrderService لتتبع تدفق تقديم الطلبات بالكامل (Controller → Service → Repository)، واعرض تفاصيل التتبع في واجهة Jaeger.



