الإعداد والملفّات الشخصية وActuator

المقاييس مع Micrometer

18 دقيقة الدرس 8 من 13

المقاييس مع Micrometer

معرفة أن تطبيقك يعمل (فحص الصحة) أمر ضروري لكنه غير كافٍ. لتشغيل خدمة إنتاجية بثقة تحتاج إلى الإجابة عن أسئلة كهذه: كم طلبًا في الثانية يعالجه هذا المسار؟ ما زمن الاستجابة عند النسبة المئوية التسعينية والخامسة؟ ما مدى امتلاء تجمّع اتصالات قاعدة البيانات؟ تأتي هذه الإجابات من المقاييس (Metrics)، والمكتبة التي تجمعها وتصدّرها في Spring Boot 3 هي Micrometer.

ما هو Micrometer

Micrometer هو واجهة مقاييس محايدة من ناحية المورّد — تعمل نفس واجهة البرمجة سواء صدّرت البيانات إلى Prometheus أو Datadog أو InfluxDB أو CloudWatch أو عشرات الخلفيات الأخرى. تسحب تبعية Spring Boot Actuator مكتبة Micrometer تلقائيًا؛ لا تحتاج إلى إضافتها منفردةً.

Micrometer للمقاييس هو كـ SLF4J للتسجيل. اكتب مرةً واحدة بواجهة Micrometer؛ بدّل خلفية المراقبة بتغيير تبعية وبضعة خصائص — دون أي تغييرات في كود التطبيق.

الكائن المحوري هو MeterRegistry. تُهيّئه Spring Boot تلقائيًا وتضيفه إلى سياق التطبيق. احقنه أينما احتجت إلى تسجيل قياسات.

المسار /metrics

عندما يكون spring-boot-starter-actuator موجودًا في مسار الفئات، يصبح المسار /actuator/metrics متاحًا (GET HTTP). يسرد جميع أسماء العدّادات التي سجّلتها Micrometer:

GET /actuator/metrics { "names": [ "application.started.time", "disk.free", "disk.total", "http.server.requests", "jvm.buffer.count", "jvm.gc.pause", "jvm.memory.max", "jvm.memory.used", "jvm.threads.live", "process.cpu.usage", "system.cpu.count", "hikaricp.connections.active", "hikaricp.connections.idle" ] }

تعمّق في عدّاد بعينه بإلحاق اسمه:

GET /actuator/metrics/jvm.memory.used { "name": "jvm.memory.used", "description": "The amount of used memory", "baseUnit": "bytes", "measurements": [ { "statistic": "VALUE", "value": 83886080 } ], "availableTags": [ { "tag": "area", "values": ["heap", "nonheap"] }, { "tag": "id", "values": ["G1 Eden Space", "Metaspace", ...] } ] }

صفّ بوسم (tag) باستخدام معامل استعلام: /actuator/metrics/jvm.memory.used?tag=area:heap

العدّادات المدمجة

تسجّل Spring Boot مجموعة ثرية من العدّادات تلقائيًا — تحصل على جميع ما يلي دون أي إعداد:

  • مقاييس JVM (jvm.*) — مناطق الذاكرة، عدد توقفات GC ومددها، حالات الخيوط، تحميل الفئات.
  • مقاييس خادم HTTP (http.server.requests) — عدد الطلبات والوقت الكلي والحد الأقصى، موسومة بـ URI والطريقة ورمز حالة HTTP.
  • مقاييس العملية (process.*) — استخدام المعالج، وقت التشغيل، واصفات الملفات.
  • مقاييس النظام (system.*) — عدد وحدات المعالجة، متوسط الحمل، مساحة القرص.
  • مقاييس HikariCP (hikaricp.*) — حجم التجمّع، الاتصالات النشطة والخاملة، طلبات الانتظار، انتهاء المهل.
  • مقاييس Logback (logback.events) — عدد الأحداث بحسب المستوى (INFO وWARN وERROR).

تسجيل مقاييس مخصصة

تغطّي أنواع العدّادات الأربعة الرئيسية كل احتياجات القياس تقريبًا:

  • Counter — قيمة تتزايد بصورة رتيبة. استخدمه للأحداث: الطلبات المُرسَلة، الرسائل المُرسَلة، الأخطاء الملتقَطة.
  • Gauge — لقطة حالية قد ترتفع أو تنخفض. استخدمه للأحجام: عمق طابور المهام، حجم الذاكرة المؤقتة، الجلسات النشطة.
  • Timer — يسجّل المدد والأعداد. استخدمه للعمليات ذات الكمون: استدعاءات HTTP، استعلامات قاعدة البيانات.
  • DistributionSummary — يسجّل توزيعًا للقيم دون وحدة زمنية. استخدمه للأحجام: بايتات الحمولة، أحجام الدُّفعات.

مثال Counter

import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Service; @Service public class OrderService { private final Counter ordersPlaced; public OrderService(MeterRegistry registry) { this.ordersPlaced = Counter.builder("orders.placed") .description("Total number of orders placed") .tag("channel", "web") .register(registry); } public void placeOrder(Order order) { // ... منطق الأعمال ... ordersPlaced.increment(); } }

مثال Timer

import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Service; @Service public class PaymentService { private final Timer paymentTimer; public PaymentService(MeterRegistry registry) { this.paymentTimer = Timer.builder("payment.processing") .description("Time spent processing payments") .tag("provider", "stripe") .publishPercentiles(0.5, 0.95, 0.99) // p50, p95, p99 .register(registry); } public PaymentResult charge(PaymentRequest req) { return paymentTimer.record(() -> { // ... استدعاء مزوّد الدفع ... return new PaymentResult(); }); } }

مثال Gauge

import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Component; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @Component public class TaskQueue { private final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(); public TaskQueue(MeterRegistry registry) { Gauge.builder("task.queue.size", queue, BlockingQueue::size) .description("Number of pending tasks in the queue") .register(registry); } public void enqueue(Runnable task) { queue.offer(task); } }
فضّل حقن العدّادات في المُنشئ. أنشئ العدّاد وسجّله مرةً واحدة في المُنشئ، ثم استدع increment() أو record() مرات كثيرة لاحقًا. إنشاء كائن عدّاد جديد عند كل استدعاء للدالة مُضيعة للموارد ويُسبّب تحذيرات تسجيل مكرّر.

الوسوم (Tags): مفتاح لوحات المعلومات المفيدة

الأعداد الخام نادرًا ما تكون مفيدةً بذاتها. تحوّل الوسوم العدّادَ الواحد إلى مجموعة بيانات متعددة الأبعاد. حين توسم orders.placed بـ channel=web وchannel=mobile بصورة منفصلة، تستطيع أداة المراقبة تجميع السلاسل أو تقسيمها حسب الحاجة.

أبقِ الوسوم ذات cardinality منخفضة — مجموعة صغيرة ومحدودة من القيم. لا تستخدم معرّفات المستخدمين أو الطلبات أو أي قيمة غير محدودة وسمًا. كل تركيبة فريدة من الوسوم تُنشئ سلسلة زمنية جديدة في خلفية المراقبة؛ تفجير cardinality يُدمّر التخزين وتكاليف الاستعلام.

الوسوم ذات الـ cardinality العالي تُدمّر أنظمة المراقبة. وسم كـ tag("userId", userId.toString()) يُنشئ ملايين السلاسل الفريدة. استخدم القيم التصنيفية المنخفضة فقط: رموز الحالة، أسماء المناطق، أعلام الميزات.

التصدير إلى Prometheus

مسار /actuator/metrics مناسب للفحص الفوري، لكن أنظمة المراقبة الإنتاجية تسحب المقاييس بالجملة. Prometheus هو الخيار الأكثر شيوعًا. أضف تبعية السجل:

<!-- pom.xml --> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>

تُهيّئ Spring Boot سجل Prometheus تلقائيًا وتعرض مسار /actuator/prometheus بتنسيق نص Prometheus. أضف سطرًا واحدًا إلى إعداد السحب لديك وسيتدفق كل عدّاد سجّلته — المدمج والمخصص — إلى Prometheus تلقائيًا.

# application.properties management.endpoints.web.exposure.include=health,info,metrics,prometheus

المزالق الشائعة

  • نسيان استدعاء .register(registry) — يصمت العدّاد ولا يظهر أبدًا في /actuator/metrics.
  • قياس الوقت يدويًا بـ System.currentTimeMillis() بدلًا من استخدام Timer — تفقد النسب المئوية وحاويات الرسم البياني وتجريد ساعة Micrometer القابلة للاستبدال في الاختبارات.
  • استخدام @Timed على حبة غير مُدارة من Spring — الوكيل AOP الذي يُشغّل التعليق التوضيحي يُنشأ فقط لحبوب Spring.

الخلاصة

تمنح Micrometer تطبيقات Spring Boot 3 طبقة مقاييس ثرية ومحايدة من ناحية المورّد. تحصل على عدّادات JVM وHTTP والتجمّع والنظام مجانًا؛ أضف عدّادات Counter وTimer وGauge وDistributionSummary الخاصة بنطاق عملك عبر MeterRegistry المحقون. أبقِ الوسوم منخفضة الـ cardinality، انشر النسب المئوية على العدّادات الزمنية الحساسة للكمون، وصدّر إلى Prometheus (أو أي سجل آخر) بتبعية واحدة. الدرس التالي يتناول تأمين مسارات Actuator وتخصيصها لضمان وصول المستهلكين المناسبين فقط.