أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

OpenTelemetry للمحترف: لاحق الـ 200ms المختفية بين 6 خدمات

📅 ٢٩ أبريل ٢٠٢٦⏱ 6 دقائق قراءة
OpenTelemetry للمحترف: لاحق الـ 200ms المختفية بين 6 خدمات
هذا المقال للمحترف. يفترض إنك شغّال على نظام microservices إنتاجي، بتفرّق بين log و metric و trace، وعندك خبرة سابقة مع Prometheus أو Grafana. لو لسه على monolith واحد بدون calls خارجية، الـ trade-offs اللي هنتكلم فيها مش هتفرق معاك.

OpenTelemetry بالعربي: لاحق الـ 200ms المختفية بين 6 خدمات microservices

السيرفر بيرجّع الـ API في 1.4 ثانية. Prometheus بيقولك إن DB latency متوسطها 80ms والـ CPU عند 12%. Grafana نضيفة. Logs ما فيهاش errors. لكن الزائر بيشتكي. السبب بالظبط: 200ms بتضيع كل request بين خدمتين من الستة، وانت مش شايفها لأن الـ metrics بتشوف averages، مش spans فردية.

OpenTelemetry بيقفل الفجوة دي بسطر instrumentation واحد لكل خدمة: بياخد الطلب من لحظة دخوله للـ gateway، يلاحقه عبر كل خدمة وكل DB query وكل Redis call، ويرسمه كـ waterfall بيقولك أي خدمة أكلت كام millisecond بالظبط.

المشكلة باختصار

الفرق بين الثلاثة بسيط: Metrics بتقول لك "في مشكلة". Logs بتقول لك "في error". Traces بتقول لك "في الخدمة X، خد Z millisecond، والسبب call لـ Redis اتعمله timeout بعد retry فشل". الفرق هنا هو نفس الفرق بين "العربية بتسخن" وبين "الفان بلت محتاج تغيير".

في أنظمة microservices، طلب واحد ممكن يمر على 5 لـ 12 خدمة. من غير tracing، انت في الظلام. ضربة console.log في كل خدمة + إعادة deploy = ساعتين شغل لتشخيص حاجة كان trace كامل هيقولها في 30 ثانية.

مثال للمبتدئ — شركة الشحن

تخيّل شحنة من الإسكندرية للرياض. الشحنة بتمر على 5 محطات: مخزن المنشأ، الشاحنة، الميناء، الجمارك، شركة التوصيل المحلية. كل محطة بتحط ختم على ورقة الشحنة فيه وقت الدخول والخروج، ورقم الشحنة الموحد على كل الأختام.

لو الشحنة وصلت متأخرة 6 ساعات، الورقة دي بالظبط هي اللي بتقول لك "اتأخرت 5 ساعات في الجمارك"، مش "في حتة في السكة". OpenTelemetry trace هو نفس ورقة الشحنة دي للطلب اللي بيمر على خدماتك. كل محطة = service. كل ختم = span. وقت الدخول والخروج = start_time و end_time. ورقم الشحنة الموحد على كل الأختام = trace_id.

التعريف العلمي الدقيق

بعد ما المثال وضّح الفكرة، دلوقتي التعريف بدقة:

  • Trace: مجموعة spans مرتبطة بنفس الـ trace_id (UUID 128-bit) تمثل دورة حياة طلب واحد عبر النظام.
  • Span: وحدة عمل واحدة (HTTP call، DB query، function execution). بيحتوي على span_id، parent_span_id (لبناء الشجرة)، start_time/end_time، attributes زي http.method و db.statement، و events (لحظات داخل الـ span زي retry أو cache miss).
  • Context Propagation: الميكانيزم اللي بيمشّي الـ trace_id من خدمة لخدمة عبر HTTP headers. القاعدة المعيارية اسمها W3C Trace Context وبتستخدم header traceparent. من غيره، كل خدمة هتعمل trace منفصل ومش هتعرف تربطهم.
لوحة مراقبة بيانات تعرض رسومًا بيانية لقياس أداء الخدمات الموزعة

مثال تنفيذي — instrumentation Node.js في 4 سطور

ملف tracing.js منفصل عن business logic:

JavaScript
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4318/v1/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations({
    '@opentelemetry/instrumentation-fs': { enabled: false },
  })],
});

sdk.start();

شغّل التطبيق بـ node --require ./tracing.js app.js. كل request HTTP، كل query لـ pg أو mongo، كل call لـ Redis، هيتسجل تلقائيًا. مش محتاج تعدّل سطر business logic واحد.

علشان الـ trace_id يمشي بين الخدمات، التطبيق التاني لازم يكون عنده نفس الـ instrumentation. الـ HTTP client بيحط traceparent تلقائيًا، والـ HTTP server بيقراه ويبني span ابن من نفس الـ trace.

إعداد Collector و Jaeger في docker compose

YAML
services:
  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.95.0
    command: --config=/etc/otel-config.yaml
    volumes:
      - ./otel-config.yaml:/etc/otel-config.yaml
    ports: ["4318:4318"]
  jaeger:
    image: jaegertracing/all-in-one:1.56
    ports: ["16686:16686", "4317:4317"]
YAML
# otel-config.yaml
receivers:
  otlp:
    protocols:
      http: { endpoint: 0.0.0.0:4318 }
exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls: { insecure: true }
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlp/jaeger]

افتح http://localhost:16686، اختار خدمتك، شوف الـ trace كاملة كـ waterfall.

شاشة كود برمجي تعرض تتبع الطلبات بين الخدمات الميكروية

أرقام حقيقية من الإنتاج

في تطبيق e-commerce بـ 6 خدمات (gateway ← auth ← product ← inventory ← pricing ← cart) جالنا P95 latency = 1.4 ثانية. فتحنا Jaeger وشفنا التوزيع التالي:

  • gateway: 12ms — سليمة.
  • auth: 45ms — سليمة.
  • product: 980ms — المشكلة هنا.
  • inventory: 90ms — مقبولة.
  • pricing: 240ms — في retries على Redis.
  • cart: 33ms — سليمة.

في خدمة product: query SQL بيرجّع 1200 صف وفيه N+1 على relation الـ images عبر الـ ORM. حلّيناها بـ eager loading. الـ trace اللي بعدها: P95 = 280ms. الـ pricing: بدّلنا الـ retry strategy من 3 retries × 80ms لـ circuit breaker، نزّل الزمن لـ 60ms. النتيجة الكلية: 1.4s → 380ms في يومين شغل. من غير tracing، الكشف ده كان هياخد أسبوع كامل من console.log و redeploy.

الـ trade-offs اللي لازم تحسبها

  • Overhead: 2–7% CPU و 50–200 bytes per span على الذاكرة. لو عندك sustained throughput فوق 5,000 RPS per node، احسب التكلفة بدقة قبل ما تفعّل auto-instrumentation كامل.
  • التخزين: trace فيها 6 spans = 5 لـ 15KB raw. عند 2,000 RPS، ده يعني حوالي 1TB يوميًا قبل أي ضغط. الحل: head sampling (احتفظ بـ 10% عشوائي) أو tail sampling (احتفظ بكل trace فيها error أو latency > P95).
  • Noisy auto-instrumentation: instrumentation-fs و dns بيضيفوا spans كتيرة من غير قيمة. اقفلهم explicit زي ما عملت في الكود فوق.
  • Vendor lock-in عكسي: OpenTelemetry معياري، تقدر تبدّل الـ backend من Jaeger لـ Tempo لـ Honeycomb من غير ما تلمس كود التطبيق. ده مكسب نادر في عالم المراقبة.

متى لا تستخدم OpenTelemetry

  • لو تطبيقك monolith بدون calls خارجية: structured logs + metrics بتكفي.
  • لو الـ throughput منخفض (تحت 10 RPS) والـ debugging اليدوي ممكن: التكلفة العملية مش مبررة.
  • لو الفريق مش هيستثمر في dashboards وalerting على traces: هتجمع بيانات مش بتنظر لها.
  • لو كامل الـ stack على Lambda بـ cold starts متكررة: الـ SDK ممكن يضاعف cold start time. استخدم AWS X-Ray native بدل OTel SDK الكامل.

الخطوة التالية

افتح أبسط خدمة في النظام عندك، ضيف الـ 4 سطور instrumentation اللي فوق، شغّل Jaeger في Docker على جهازك المحلي، وابعت 100 request. شوف الـ waterfall. اللي هتلاقيه في الـ trace الأولى هيغيّر فهمك للنظام أكتر من شهر متابعة dashboards.

المصادر

  • OpenTelemetry Documentation — Concepts: opentelemetry.io/docs/concepts
  • W3C Trace Context Specification: w3.org/TR/trace-context
  • Jaeger Architecture Overview: jaegertracing.io/docs/1.56/architecture
  • Google Dapper Paper (2010) — أصل distributed tracing: research.google/pubs/dapper
  • OpenTelemetry Collector — Tail Sampling Processor: github.com/open-telemetry/opentelemetry-collector-contrib
]]>

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة