التتبع الموزع
التتبع الموزع
عندما يتفرّع طلب HTTP واحد عبر أربع خدمات مصغّرة قبل أن يُعيد الاستجابة، يكاد يكون تشخيص بطء وقت الاستجابة أو خطأ 500 متقطّع أمرًا مستحيلًا بالاعتماد على السجلات العادية. فكل خدمة تكتب أسطر سجلها الخاصة، على ساعتها الخاصة، بتنسيقها الخاص. إن ربطها يدويًا عملية معرّضة للخطأ وبطيئة. يحلّ التتبع الموزع هذه المشكلة بإرفاق معرّف فريد بكل طلب عند نقطة دخوله ونشر هذا المعرّف — تلقائيًا — عبر كل استدعاء لاحق. والنتيجة جدول زمني كامل وسببي يوضّح بالضبط أين أمضى الطلب وقته وأين فشل.
المصطلحات الأساسية
قبل كتابة أي كود من المفيد توضيح المصطلحات:
- الأثر (Trace) — رحلة الطلب المنطقي الواحد من بدايتها حتى نهايتها. يُعرَّف بمعرّف فريد عالميًا هو
traceId. - الامتداد (Span) — وحدة عمل مسمّاة ومحدودة بالوقت ضمن الأثر. كل قفزة بين الخدمات وكل استدعاء لقاعدة بيانات وكل عملية مهمة تُمثَّل على شكل امتداد. يعرف الامتداد
traceIdوهويتهspanIdوهوية الامتداد الأبparentSpanId. - نشر السياق (Context propagation) — الآلية التي تحمل
traceIdوspanIdعبر حدود الخدمات، عادةً كترويسات HTTP مثلtraceparent(معيار W3C) أوX-B3-TraceId(تراث Zipkin/Brave). - المُصدِّر (Exporter) — المكوّن الذي يرسل الامتدادات المكتملة إلى الخلفية الداعمة (Zipkin أو Jaeger أو جامع متوافق مع OTLP مثل Grafana Tempo).
Micrometer Tracing في Spring Boot 3
استبدل Spring Boot 3 مكتبة Spring Cloud Sleuth القديمة بـ Micrometer Tracing، وهي واجهة محايدة وخفيفة فوق تنفيذات التتبع (Brave/Zipkin أو OpenTelemetry). أضف التالي إلى pom.xml:
micrometer-tracing-bridge-otel إذا كانت مؤسستك تستخدم بالفعل OpenTelemetry Collector، إذ يتحوّل OTLP بسرعة إلى المعيار العالمي. أما Brave فأسهل للبدء وله حجم تبعيات أصغر.
الضبط الأدنى
بمجرد وجود تلك التبعيات على مسار الفئات، تُفعِّل الضبط التلقائي التتبع. اضبطه في application.yml:
يضخّ نمط التسجيل traceId وspanId في كل سطر سجل تلقائيًا. عند فشل طلب يمكنك نسخ traceId من أي سطر سجل وفتح واجهة Zipkin لرؤية المخطط الانسيابي الكامل.
الأدوات التلقائية
معظم أدوات Spring Boot لا تحتاج كودًا. بمجرد وجود التبعيات:
- طلبات HTTP الواردة — يبدأ
TracingFilter(servlet) أوTracingWebFilter(تفاعلي) أثرًا جديدًا أو ينضم إلى أثر موجود إن وُجدت ترويسةtraceparent، ويُغلق الامتداد عند إرسال الاستجابة. - استدعاءات HTTP الصادرة عبر
RestTemplateأوWebClient— تضيف الضبط التلقائي مُعترِضًا يحقن ترويسات نشر السياق في كل طلب صادر، فتشارك الخدمة اللاحقة تلقائيًا في الأثر ذاته. - Spring Data / JDBC — عند وجود
spring-boot-starter-data-jpa، تظهر استدعاءات قاعدة البيانات كامتدادات ابن مسمّاة حسب الاستعلام. - مُستمعو الرسائل (Kafka، RabbitMQ) — تحمل ترويسات سجل الرسالة سياق الأثر، ويلتقطه أداة المستمع تلقائيًا.
RestTemplate أو WebClient دائمًا عبر البنّاء المُضبَط مسبقًا. إنشاء new RestTemplate() مباشرةً يتجاوز مُعترِض التتبع. بدلًا من ذلك أدخل RestTemplateBuilder (متزامن) أو استخدم WebClient.Builder التلقائي (تفاعلي) — فكلاهما مُهيَّأ مسبقًا مع مرشّح التتبع.
إنشاء امتدادات مخصصة
تغطي الأدوات التلقائية طبقة البنية التحتية. للعمليات التجارية المكلفة أو المعرّضة للفشل — استدعاء واجهة خارجية أو حساب معقد أو بحث في ذاكرة التخزين المؤقت — تريد امتدادًا مخصصًا يُظهر المخطط الانسيابي بالضبط كم استغرق. أدخل Tracer واستخدم الواجهة السلسة:
بعض النقاط المهمة في هذا النمط:
tracer.nextSpan()ينشئ امتدادًا ابنًا من الامتداد النشط الحالي، فيندرج بشكل صحيح في تسلسل الأثر.span.tag()يرفق بيانات وصفية من نوع مفتاح-قيمة تظهر في تفاصيل الامتداد في Zipkin/Jaeger — قيّمة للغاية لتصفية الآثار حسب المنتج أو المستخدم أو المستأجر أو أي بُعد تجاري.span.error(ex)يسجّل الاستثناء ويضع حالة الامتداد كـ ERROR، مما يجعله يبرز فورًا في واجهة التتبع.- كتلة
finallyإلزامية؛ فالامتداد غير المُغلق يُسرّب الذاكرة ولا يصل أبدًا إلى المُصدِّر.
الاعتبارات الأمنية
traceparent مصنوعة، ستنضم خدماتك إلى ذلك الأثر — مما قد يُسرّب طبولوجيا الخدمات الداخلية لمهاجم يستطيع ربط بيانات التوقيت. خفّف هذا الخطر بالثقة في سياق الأثر الوارد فقط من المُستدعين الداخليين الموثّقين (مثل الخدمات التي تقدّم شهادة mTLS صالحة أو JWT خاص بالخدمة). عند الحافة (بوابة API)، اشطب ترويسات الأثر الواردة من الإنترنت العام وأعد إصدارها.
كذلك انتبه لما تُرفقه كوسوم للامتداد. الوسم مثل user.id أو request.body يُخزَّن حرفيًا في الخلفية الداعمة للتتبع. عامل نظام التتبع كمخزن للرصد لا للتسجيل، وتجنّب إرفاق البيانات الشخصية أو الأسرار كسمات للامتداد.
استراتيجية أخذ العينات
تتبع 100 % من الطلبات مناسب في التطوير. في الإنتاج بحجم مهم، تصدير كل امتداد إلى خلفية ينشئ تكلفة وتخزينًا غير هيّنين. الاستراتيجيات الشائعة:
- الاحتمالية (head-based) — أخذ عينة بنسبة ثابتة (مثل 10 %) تُقرَّر عند جذر الأثر. بسيطة وتكلفتها قابلة للتنبؤ. تُضبط بـ
management.tracing.sampling.probability=0.1. - محدودة المعدل — أخذ عينة لا تزيد عن N أثر في الثانية بصرف النظر عن الحمل. تحمي الخلفية خلال ارتفاعات حركة المرور.
- القائمة على الذيل (tail-based) — تخزين جميع الامتدادات مؤقتًا والاحتفاظ فقط بالآثار التي تحتوي على خطأ أو تتجاوز حد الاستجابة. تتطلب جامعًا يدعم أخذ عينات الذيل (مثل OpenTelemetry Collector مع معالج
tail_sampling). أكثر تعقيدًا تشغيليًا لكنها تلتقط 100 % من الآثار المثيرة للاهتمام دون تكلفة تصدير 100 %.
تشغيل Zipkin محليًا
يمكنك تشغيل نسخة Zipkin في ثوانٍ باستخدام Docker:
وجّه خدمتك إلى http://localhost:9411، أجرِ عدة استدعاءات HTTP، ثم افتح http://localhost:9411 في المتصفح. اضغط Run Query لرؤية جميع الآثار، ثم اضغط على أي أثر لعرض مخططه الانسيابي. كل امتداد مسمّى باسم الخدمة واسم العملية والمدة وأي وسوم أضفتها.
الخلاصة
يحوّل التتبع الموزع بحر أسطر السجلات المنفصلة إلى جدول زمني منظَّم وبصري لكل طلب. مع Micrometer Tracing وثلاث تبعيات Maven، يُوثّق Spring Boot 3 تلقائيًا جميع حركة HTTP للخادم والعميل واستدعاءات قاعدة البيانات ومستمعي الرسائل. أضف امتدادات مخصصة للعمليات التجارية المهمة باستخدام واجهة Tracer، ارفق وسومًا ذات معنى، أغلق الامتدادات دائمًا في كتلة finally، واختر استراتيجية أخذ عينات توازن بين الرؤية والتكلفة. في الدرس القادم ستتعلّم كيف تُكمّل الآثار بمقاييس ولوحات صحية لإكمال صورة الرصد.