انتقل إلى المحتوى الرئيسي

TP13.2: خادم تكوين Spring Cloud

🎯 أهداف التمرين​

  • إنشاء خادم تكوين مع Git كخلفية
  • تكوين الخدمات كـ عملاء تكوين
  • تنفيذ التحديث للتكوين في الوقت الفعلي
  • إدارة ملفات التعريف (dev, prod)

المدة المقدرة: 1 ساعة


📋 الهندسة المعمارية​


الجزء 1: إنشاء مستودع التكوين (15 دقيقة)​

1.1 إنشاء مستودع Git محلي (أو GitHub)​

# إنشاء المجلد
mkdir config-repo
cd config-repo
git init

# إنشاء ملفات التكوين

1.2 التكوين المشترك (application.yml)​

# config-repo/application.yml
# تكوين مشترك بين جميع الخدمات

eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true

management:
endpoints:
web:
exposure:
include: health,info,refresh

logging:
level:
root: INFO

1.3 تكوين خدمة المستخدم​

# config-repo/user-service.yml
server:
port: 8081

user:
default-role: USER
password-min-length: 8
welcome-message: "Welcome to User Service!"
# config-repo/user-service-dev.yml
user:
welcome-message: "Welcome to User Service (DEV)!"

logging:
level:
com.example: DEBUG
# config-repo/user-service-prod.yml
user:
welcome-message: "Welcome to User Service (PRODUCTION)"
password-min-length: 12

logging:
level:
com.example: WARN

1.4 تكوين خدمة المنتج​

# config-repo/product-service.yml
server:
port: 8083

product:
max-per-page: 20
currency: CAD
featured-enabled: true
# config-repo/product-service-dev.yml
product:
currency: DEV-CAD
featured-enabled: true

logging:
level:
com.example: DEBUG

1.5 تسجيل الملفات​

cd config-repo
git add .
git commit -m "Initial configuration files"

الجزء 2: خادم التكوين (20 دقيقة)​

2.1 إنشاء المشروع​

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>

<groupId>com.example</groupId>
<artifactId>config-server</artifactId>
<version>1.0.0</version>

<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

2.2 التطبيق الرئيسي​

package com.example.configserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

2.3 التكوين (مع Git محلي)​

# src/main/resources/application.yml
server:
port: 8888

spring:
application:
name: config-server
cloud:
config:
server:
git:
# المسار إلى مستودعك المحلي (Windows)
uri: file:///C:/chemin/vers/config-repo
# أو مسار Linux/Mac
# uri: file://${user.home}/config-repo
default-label: master
clone-on-start: true

eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/

2.4 تكوين بديل (GitHub)​

spring:
cloud:
config:
server:
git:
uri: https://github.com/votre-username/config-repo
default-label: main
# إذا كان المستودع خاصًا:
# username: ${GIT_USERNAME}
# password: ${GIT_TOKEN}

2.5 اختبار خادم التكوين​

# بدء خادم التكوين
cd config-server
mvn spring-boot:run

# اختبار النقاط النهائية
# الصيغة: /{application}/{profile}

curl http://localhost:8888/user-service/default
curl http://localhost:8888/user-service/dev
curl http://localhost:8888/user-service/prod
curl http://localhost:8888/product-service/dev

الاستجابة المتوقعة لـ /user-service/dev:

{
"name": "user-service",
"profiles": ["dev"],
"label": null,
"version": "abc123...",
"propertySources": [
{
"name": "file:///C:/config-repo/user-service-dev.yml",
"source": {
"user.welcome-message": "Welcome to User Service (DEV)!",
"logging.level.com.example": "DEBUG"
}
},
{
"name": "file:///C:/config-repo/user-service.yml",
"source": {
"server.port": 8081,
"user.default-role": "USER"
}
},
{
"name": "file:///C:/config-repo/application.yml",
"source": {
"eureka.client.service-url.defaultZone": "http://localhost:8761/eureka/"
}
}
]
}

الجزء 3: عميل التكوين (خدمة المستخدم) (15 دقيقة)​

3.1 إضافة التبعيات​

عدل pom.xml لخدمة المستخدم:

<dependencies>
<!-- موجودة... -->

<!-- عميل التكوين -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

<!-- Bootstrap (لتحميل التكوين قبل التطبيق) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>

3.2 إنشاء bootstrap.yml​

# src/main/resources/bootstrap.yml
spring:
application:
name: user-service
profiles:
active: dev
cloud:
config:
uri: http://localhost:8888
fail-fast: true
retry:
initial-interval: 1000
max-attempts: 6

3.3 تبسيط application.yml المحلي​

# src/main/resources/application.yml
# معظم التكوين يأتي الآن من خادم التكوين
# نحتفظ فقط بما هو محلي/تجاوز

# لا شيء أو تقريبًا - كل شيء يأتي من خادم التكوين!

3.4 استخدام الخصائص الخارجية​

package com.example.userservice.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/config")
@RefreshScope // يسمح بالتحديث في الوقت الفعلي!
public class ConfigController {

@Value("${user.welcome-message}")
private String welcomeMessage;

@Value("${user.default-role}")
private String defaultRole;

@Value("${user.password-min-length}")
private int passwordMinLength;

@GetMapping
public ResponseEntity<Map<String, Object>> getConfig() {
return ResponseEntity.ok(Map.of(
"welcomeMessage", welcomeMessage,
"defaultRole", defaultRole,
"passwordMinLength", passwordMinLength
));
}

@GetMapping("/welcome")
public ResponseEntity<String> getWelcome() {
return ResponseEntity.ok(welcomeMessage);
}
}

3.5 اختبار​

# ترتيب البدء:
# 1. خادم Eureka (8761)
# 2. خادم التكوين (8888)
# 3. خدمة المستخدم (8081)

cd user-service
mvn spring-boot:run

# تحقق من تحميل التكوين
curl http://localhost:8081/api/config

النتيجة المتوقعة:

{
"welcomeMessage": "Welcome to User Service (DEV)!",
"defaultRole": "USER",
"passwordMinLength": 8
}

الجزء 4: التحديث في الوقت الفعلي (10 دقائق)​

4.1 تعديل التكوين​

حرر config-repo/user-service-dev.yml:

user:
welcome-message: "Welcome to User Service (DEV) - UPDATED!"

logging:
level:
com.example: DEBUG
cd config-repo
git add .
git commit -m "Update welcome message"

4.2 التحقق قبل التحديث​

curl http://localhost:8081/api/config/welcome
# النتيجة: "Welcome to User Service (DEV)!"
# (القيمة القديمة - لم يتم تحديثها بعد)

4.3 تفعيل التحديث​

curl -X POST http://localhost:8081/actuator/refresh

الاستجابة:

["user.welcome-message"]

4.4 التحقق بعد التحديث​

curl http://localhost:8081/api/config/welcome
# النتيجة: "Welcome to User Service (DEV) - UPDATED!"

🎉 تم تحديث التكوين دون إعادة تشغيل الخدمة!


✅ قائمة التحقق من التحقق​

  • خادم التكوين يبدأ على http://localhost:8888
  • curl http://localhost:8888/user-service/dev يعيد التكوين
  • خدمة المستخدم تحمل تكوينها من خادم التكوين
  • /api/config يعرض الخصائص الخارجية
  • تعديل + تسجيل + /actuator/refresh → تكوين جديد

🎯 تمارين إضافية​

تمرين 1: ملف التعريف للإنتاج​

ابدأ خدمة المستخدم مع ملف التعريف prod:

mvn spring-boot:run -Dspring-boot.run.arguments=--spring.profiles.active=prod

تحقق من أن passwordMinLength هو 12.

تمرين 2: الاكتشاف عبر Eureka​

بدلاً من عنوان URL ثابت، استخدم Eureka:

# bootstrap.yml
spring:
cloud:
config:
discovery:
enabled: true
service-id: config-server

تمرين 3: تكوين خدمة المنتج​

قم بتكوين خدمة المنتج كعميل تكوين وكشف عن /api/config.


TP13.3: بوابة API →

العودة إلى الوحدة 13