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

مشروع: إعداد جاهز للإنتاج

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

مشروع: إعداد جاهز للإنتاج

على مدار هذا البرنامج التعليمي تعلّمت كل مكوّن بمعزل عن غيره: الإعداد الخارجي، والإعداد الآمن من النوع بواسطة @ConfigurationProperties، وملفات التعريف في Spring Boot، وصيغة YAML، والأسرار من متغيرات البيئة، ونقاط نهاية Actuator، ومؤشرات الصحة، ومقاييس Micrometer، وتأمين طبقة الإدارة. يجمع هذا الدرس الختامي كلّ هذه القطع في تطبيق Spring Boot 3 متسق وموحّد يمكنك تسليمه لفريق العمليات يوم الاثنين صباحًا. الهدف ليس تقديم واجهات برمجية جديدة — بل إظهار كيف تتلاءم القطع معًا ومناقشة المقايضات الإنتاجية الأكثر أهمية.

نظرة عامة على المشروع

سنبني خدمة معالجة الطلبات في أبسط صورها: واجهة REST API تستقبل الطلبات وتحفظها عبر مستودع JPA، وتكشف مؤشر صحة Actuator مخصصًا ومقياس Micrometer. الدرس الحقيقي يكمن في البنية التحتية للإعداد المحيطة بها.

ركّز على البنية التحتية، لا على نطاق المجال. نطاق الطلبات بسيط عمدًا. ما يهمّ هو فهم كيفية ربط الإعداد وملفات التعريف والأسرار والمراقبة معًا حتى يُطبَّق نفس النمط على أي خدمة تبنيها.

الخطوة 1 — حبّة الإعداد الآمن من النوع

تعيش جميع مفاتيح الضبط الخاصة بالتطبيق في POJO واحد محقَّق. لا تبعثر @Value عبر عشرات الفئات — فذلك يجعل إعادة البناء مؤلمة ويُخفي السطح الكامل للإعداد عن القارئ.

package com.example.orders.config; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.validation.annotation.Validated; @Validated @ConfigurationProperties(prefix = "app") public record AppProperties( @NotBlank String name, @NotBlank String downstreamApiUrl, @Min(1) int maxOrdersPerMinute, boolean detailedErrors ) {}

فعّلها في الفئة الرئيسية (أو في أي فئة @Configuration):

@SpringBootApplication @EnableConfigurationProperties(AppProperties.class) public class OrdersApplication { public static void main(String[] args) { SpringApplication.run(OrdersApplication.class, args); } }

الخطوة 2 — إعداد YAML بملفات التعريف

استخدم ثلاثة ملفات YAML: ملف أساسي يحمل القيم الافتراضية العامة، وطبقة dev تُخفّف الأمان وتُفعّل التسجيل التفصيلي، وطبقة prod تُشدّد كل شيء.

# application.yml (الأساسي — يُحمَّل دائمًا) spring: application: name: orders-service datasource: url: ${DB_URL} username: ${DB_USER} password: ${DB_PASSWORD} jpa: open-in-view: false # لا تتركها true في الإنتاج أبدًا app: name: Orders Service downstream-api-url: ${DOWNSTREAM_API_URL} max-orders-per-minute: 100 detailed-errors: false management: endpoints: web: base-path: /manage exposure: include: health,info,metrics,prometheus endpoint: health: show-details: when_authorized info: env: enabled: true info: app: name: ${app.name} version: '@project.version@'
# application-dev.yml (يُفعَّل عبر SPRING_PROFILES_ACTIVE=dev) app: downstream-api-url: http://localhost:9090 detailed-errors: true management: endpoint: health: show-details: always # مفيد محليًا، غير آمن في الإنتاج logging: level: com.example.orders: DEBUG org.springframework.web: DEBUG
# application-prod.yml (يُفعَّل عبر SPRING_PROFILES_ACTIVE=prod) app: max-orders-per-minute: 500 spring: jpa: show-sql: false logging: level: root: WARN com.example.orders: INFO
أبقِ الأسرار بعيدًا عن YAML كليًا. كل قيمة حساسة (DB_PASSWORD، DOWNSTREAM_API_URL، مفاتيح API) يُشار إليها عبر محدّدات ${ENV_VAR} وتُحقنها منصة النشر (Kubernetes Secrets أو AWS Parameter Store أو Docker secrets). ملفات YAML تُودَع في نظام التحكم في الإصدارات — الأسرار يجب ألا تكون كذلك أبدًا.

الخطوة 3 — مؤشر صحة مخصص

يسمح HealthIndicator المخصص لـ Actuator بالإبلاغ عمّا إذا كانت تبعية المصب (downstream) قابلة للوصول. تستدعي فحوصات liveness و readiness في Kubernetes نقطة نهاية /manage/health — فمؤشر فاشل يُزيل الـ Pod من تناوب موزّع الحمل تلقائيًا.

package com.example.orders.health; import com.example.orders.config.AppProperties; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; @Component("downstreamApi") public class DownstreamApiHealthIndicator implements HealthIndicator { private final RestClient restClient; private final String pingUrl; public DownstreamApiHealthIndicator(AppProperties props) { this.pingUrl = props.downstreamApiUrl() + "/health"; this.restClient = RestClient.create(); } @Override public Health health() { try { restClient.get() .uri(pingUrl) .retrieve() .toBodilessEntity(); return Health.up() .withDetail("url", pingUrl) .build(); } catch (Exception ex) { return Health.down() .withDetail("url", pingUrl) .withDetail("error", ex.getMessage()) .build(); } } }

يكتشف Spring Boot تلقائيًا أي @Component ينفّذ HealthIndicator. يصبح اسم الحبّة ("downstreamApi") المفتاح في استجابة JSON لنقطة نهاية /manage/health.

الخطوة 4 — مقياس أعمال مع Micrometer

سجّل عدّادًا حتى يتمكّن فريق العمليات من رسم بيانات إنتاجية الطلبات في Grafana دون الحاجة إلى تحليل ملفات السجل.

package com.example.orders.service; import com.example.orders.domain.Order; import com.example.orders.domain.OrderRepository; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class OrderService { private final OrderRepository repository; private final Counter ordersCreated; public OrderService(OrderRepository repository, MeterRegistry registry) { this.repository = repository; this.ordersCreated = Counter.builder("orders.created") .description("Total orders created") .tag("service", "orders-service") .register(registry); } @Transactional public Order createOrder(Order order) { Order saved = repository.save(order); ordersCreated.increment(); return saved; } }

الخطوة 5 — تأمين نقاط نهاية الإدارة

اكشف نقاط نهاية الإدارة على منفذ منفصل حتى تستطيع جدار الحماية حجبها عن الإنترنت العام، واشترط مصادقة HTTP Basic للنقاط الحساسة.

# في application.yml — الإدارة على منفذها الخاص management: server: port: 8081
package com.example.orders.config; import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration public class ActuatorSecurityConfig { @Bean SecurityFilterChain actuatorSecurity(HttpSecurity http) throws Exception { http .securityMatcher(EndpointRequest.toAnyEndpoint()) .authorizeHttpRequests(auth -> auth .requestMatchers(EndpointRequest.to("health", "info")).permitAll() .anyRequest().hasRole("OPS") ) .httpBasic(org.springframework.security.config.Customizer.withDefaults()); return http.build(); } }
لا تكشف أبدًا /manage/env أو /manage/heapdump أو /manage/shutdown للإنترنت. تقوم نقطة نهاية env بإخراج جميع الخصائص المحلولة — بما فيها الأسرار التي قرأها Spring من متغيرات البيئة. قيّد دائمًا النقاط الحساسة للشبكات الداخلية أو أدوار المشغّل المصادق عليهم.

الخطوة 6 — نقطة نهاية سكرابينج Prometheus

إضافة سجل Micrometer الخاص بـ Prometheus إلى مسار الفئات تُنشئ تلقائيًا نقطة نهاية /manage/prometheus يمكن لـ Prometheus سكرابتها كل 15 ثانية. هذا كل ما تحتاجه في pom.xml:

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

إعداد سكرابينج Prometheus النموذجي:

# prometheus.yml scrape_configs: - job_name: 'orders-service' metrics_path: '/manage/prometheus' static_configs: - targets: ['orders-service:8081'] basic_auth: username: ops password: secret

تجميع كل شيء معًا — قائمة التحقق الإنتاجية

قبل نشر أي خدمة Spring Boot، استعرض هذه القائمة:

  1. الأسرار متغيرات بيئة — لا بيانات اعتماد في YAML أو في نظام التحكم في الإصدارات.
  2. @ConfigurationProperties مع @Validated — يفشل التشغيل مبكرًا إذا كان الإعداد المطلوب غائبًا؛ لا قيم null صامتة في وقت التشغيل.
  3. ملف التعريف النشط يُعيّنه المنصة — يُحقن SPRING_PROFILES_ACTIVE=prod بواسطة Kubernetes أو خط CI/CD، لا يُكتّب بثبات في الكود.
  4. الإدارة على منفذ منفصلmanagement.server.port=8081 يُعزل مستوى الإدارة عن مستوى التطبيق.
  5. نقطة نهاية الصحة يستخدمها المُنسِّق — فحص liveness: /manage/health/liveness؛ فحص readiness: /manage/health/readiness.
  6. مؤشر HealthIndicator مخصص لكل تبعية حرجة — قاعدة البيانات، وسيط الرسائل، وAPIs المصب.
  7. مقاييس الأعمال مسجَّلة عند التشغيل — عدّادات ومؤقتات ومقاييس قياس تُجيب على أسئلة تشغيلية دون تحليل السجلات.
  8. نقطة نهاية Prometheus محمية بـ HTTP Basic أو mTLS — يمكن للمقاييس تسريب معرفة بنطاق المجال للمهاجمين.
  9. open-in-view: false — يمنع الاستعلامات الكسولة من الإطلاق في طبقة الويب، وهو فخ N+1 شائع في تطبيقات JPA الإنتاجية.
  10. الإصدار في /manage/info — استخدم مُرشّح Maven '@project.version@' حتى يُبلّغ كل مثيل قيد التشغيل عن إصدار الأداة الدقيق الخاص به.
عامل الإعداد كمصدر كود. ملفات YAML وقواعد أمان Actuator ومؤشرات الصحة لا تقلّ أهمية عن منطق أعمالك. راجعها بنفس الصرامة واختبرها باستخدام @SpringBootTest(properties = {...}) في Spring Boot واحتفظ بها في نظام التحكم في الإصدارات جنبًا إلى جنب مع مصادر Java الخاصة بك.

الخلاصة

خدمة Spring Boot الجاهزة للإنتاج ليست مجرد منطق أعمال صحيح — إنها قابلة للمراقبة، ومُهيَّأة بأمان، وتصف نفسها تشغيليًا. بدمج @ConfigurationProperties (آمن من النوع، محقَّق، قابل لإعادة البناء)، وملفات تعريف YAML متعددة المراحل (أساسي / dev / prod)، وأسرار متغيرات البيئة، وحبّات HealthIndicator المخصصة، وعدّادات Micrometer، ومستوى إدارة Actuator المؤمَّن، تمنح فريق عملياتك كل ما يحتاجه لتشغيل الخدمة بثقة. هذا هو المعيار الذي طبّقه على كل خدمة تبنيها من الآن فصاعدًا.