الأمان والأداء

توسيع نطاق التطبيقات

20 دقيقة الدرس 30 من 35

توسيع نطاق التطبيقات

يضمن توسيع نطاق التطبيقات قدرة نظامك على التعامل مع النمو في حركة المرور والبيانات والمستخدمين مع الحفاظ على الأداء والموثوقية.

التوسع الأفقي مقابل التوسع الرأسي

استراتيجيات التوسع:
  • التوسع الرأسي (Scale Up): زيادة الموارد على خادم واحد (المزيد من CPU، RAM، القرص)
  • التوسع الأفقي (Scale Out): إضافة المزيد من الخوادم لتوزيع الحمل
  • الهجين: مزيج من كلا النهجين
// إيجابيات وسلبيات التوسع الرأسي
الإيجابيات:
- أبسط للتنفيذ
- لا حاجة لتغييرات في التطبيق
- بيانات متسقة (لا مزامنة)

السلبيات:
- حدود الأجهزة (السعة القصوى)
- نقطة فشل واحدة
- وقت تعطل أثناء الترقيات
- أكثر تكلفة على نطاق واسع

// إيجابيات وسلبيات التوسع الأفقي
الإيجابيات:
- قابلية توسع غير محدودة تقريباً
- توفر عالي (تكرار)
- فعال من حيث التكلفة (أجهزة سلعية)
- لا وقت تعطل للتوسع

السلبيات:
- تعقيد التطبيق (تصميم بدون حالة)
- تحديات مزامنة البيانات
- يتطلب موازنة الحمل
- عبء الشبكة

موازنة الحمل

توزيع حركة المرور عبر خوادم متعددة:

# تكوين موازن الحمل Nginx
upstream app_servers {
least_conn; # التوجيه إلى الخادم بأقل اتصالات

server app1.example.com:8000 weight=3;
server app2.example.com:8000 weight=2;
server app3.example.com:8000 weight=1;
server app4.example.com:8000 backup; # خادم احتياطي

# فحوصات الصحة
keepalive 32;
}

server {
listen 80;
server_name example.com;

location / {
proxy_pass http://app_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# إعدادات الاتصال
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}

خوارزميات موازنة الحمل

// Round Robin - توزيع الطلبات بشكل تسلسلي
upstream app {
server app1:8000;
server app2:8000;
server app3:8000;
}

// Least Connections - التوجيه إلى الخادم الأقل انشغالاً
upstream app {
least_conn;
server app1:8000;
server app2:8000;
}

// IP Hash - نفس العميل دائماً يذهب إلى نفس الخادم
upstream app {
ip_hash;
server app1:8000;
server app2:8000;
}

// Weighted - التوزيع حسب سعة الخادم
upstream app {
server app1:8000 weight=3; # سعة عالية
server app2:8000 weight=1; # سعة منخفضة
}

التوسع التلقائي

ضبط السعة تلقائياً بناءً على الطلب:

# سياسة التوسع التلقائي لـ AWS (مثال)
Resources:
AppAutoScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: 10
MinCapacity: 2
ResourceId: service/my-cluster/my-service
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs

ScaleUpPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: ScaleUp
PolicyType: TargetTrackingScaling
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 70.0 # التوسع عندما CPU > 70%
ScaleInCooldown: 300
ScaleOutCooldown: 60

معمارية بدون حالة

التصميم لقابلية التوسع الأفقي:

// ❌ سيء: بحالة - يخزن البيانات في ذاكرة الخادم
class SessionController
{
private $userData = []; // تخزين في الذاكرة

public function store($userId, $data)
{
$this->userData[$userId] = $data;
}
}

// ✅ جيد: بدون حالة - يخزن البيانات في ذاكرة تخزين مؤقت مشتركة
class SessionController
{
public function store($userId, $data)
{
Cache::put("user_session_{$userId}", $data, 3600);
}

public function get($userId)
{
return Cache::get("user_session_{$userId}");
}
}

// تكوين Laravel للجلسات بدون حالة
// .env
SESSION_DRIVER=redis
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis

// config/session.php
'driver' => env('SESSION_DRIVER', 'redis'),

توسيع قاعدة البيانات: النسخ المتماثل

توسيع القراءات باستخدام النسخ المتماثلة للقراءة:

// config/database.php - تكوين Master-Slave
'mysql' => [
'read' => [
'host' => [
'192.168.1.2', // نسخة قراءة 1
'192.168.1.3', // نسخة قراءة 2
],
],
'write' => [
'host' => ['192.168.1.1'], // الرئيسي
],
'sticky' => true, // اقرأ كتاباتك الخاصة
],

// استخدام اتصال القراءة صراحة
$users = DB::connection('mysql')->table('users')->get();

// فرض اتصال الكتابة
$user = DB::connection('mysql::write')->table('users')->find($id);

توسيع قاعدة البيانات: التجزئة

تقسيم البيانات عبر قواعد بيانات متعددة:

// التجزئة الأفقية حسب معرف المستخدم
class ShardedDatabase
{
const SHARD_COUNT = 4;

public static function getShardForUser($userId)
{
$shardId = $userId % self::SHARD_COUNT;
return "mysql_shard_{$shardId}";
}

public static function getUserData($userId)
{
$connection = self::getShardForUser($userId);
return DB::connection($connection)
->table('users')
->where('id', $userId)
->first();
}
}

// config/database.php - شظايا متعددة
'connections' => [
'mysql_shard_0' => [...],
'mysql_shard_1' => [...],
'mysql_shard_2' => [...],
'mysql_shard_3' => [...],
],
تحديات التجزئة: الاستعلامات عبر الشظايا مكلفة، الربط عبر الشظايا صعب، إعادة موازنة الشظايا معقدة، واختيار مفتاح الشظية الصحيح أمر حاسم.

توسيع الخدمات الصغيرة

توسيع الخدمات بشكل مستقل:

# docker-compose.yml - توسيع الخدمة المستقلة
version: '3.8'
services:
web:
image: my-app/web
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 512M

api:
image: my-app/api
deploy:
replicas: 5 # API يحتاج إلى المزيد من المثيلات
resources:
limits:
cpus: '1.0'
memory: 1G

worker:
image: my-app/worker
deploy:
replicas: 2
resources:
limits:
cpus: '2.0'
memory: 2G

توسيع CDN

توسيع تسليم المحتوى الثابت عالمياً:

// مثال تكوين CloudFront
const cloudfront = new CloudFront({
origins: [
{
id: 'S3-my-bucket',
domainName: 'my-bucket.s3.amazonaws.com',
},
],
defaultCacheBehavior: {
targetOriginId: 'S3-my-bucket',
viewerProtocolPolicy: 'redirect-to-https',
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
cachedMethods: ['GET', 'HEAD'],
compress: true,
minTTL: 0,
defaultTTL: 86400, // 24 ساعة
maxTTL: 31536000, // سنة واحدة
},
priceClass: 'PriceClass_All', // التوزيع العالمي
});

// Laravel - تقديم الأصول من CDN
// config/app.php
'asset_url' => env('ASSET_URL', 'https://cdn.example.com'),

// في قوالب blade
<img src="{{ asset('images/logo.png') }}">
// الإخراج: https://cdn.example.com/images/logo.png

التوسع القائم على قوائم الانتظار

معالجة المهام الخلفية باستخدام عمال قابلين للتوسع:

// إرسال المهام إلى قائمة الانتظار
ProcessVideoJob::dispatch($video)->onQueue('videos');
SendEmailJob::dispatch($email)->onQueue('emails');

// توسيع العمال بشكل مستقل حسب قائمة الانتظار
# بدء 10 عمال لمعالجة الفيديو
php artisan queue:work --queue=videos --tries=3 &
php artisan queue:work --queue=videos --tries=3 &
# ... (كرر 10 مرات)

# بدء 5 عمال لإرسال البريد الإلكتروني
php artisan queue:work --queue=emails --tries=3 &
# ... (كرر 5 مرات)

# تكوين Supervisor لإعادة التشغيل التلقائي
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true
numprocs=8 # عدد عمليات العمال
user=www-data

التخزين المؤقت للتوسع

تقليل حمل قاعدة البيانات بالتخزين المؤقت متعدد الطبقات:

// الطبقة 1: ذاكرة التخزين المؤقت للتطبيق (Redis)
$users = Cache::remember('active_users', 3600, function () {
return DB::table('users')->where('active', 1)->get();
});

// الطبقة 2: ذاكرة التخزين المؤقت لنتائج الاستعلام
$posts = DB::table('posts')
->where('published', 1)
->remember(3600) // التخزين المؤقت لمدة ساعة
->get();

// الطبقة 3: رؤوس ذاكرة التخزين المؤقت HTTP
return response()->view('posts.index', compact('posts'))
->header('Cache-Control', 'public, max-age=3600');

// الطبقة 4: ذاكرة التخزين المؤقت للوكيل العكسي (Varnish/Nginx)
// تكوين Nginx
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m;
location / {
proxy_cache my_cache;
proxy_cache_valid 200 1h;
proxy_pass http://app_servers;
}

مراقبة مقاييس التوسع

// المقاييس الرئيسية لمراقبتها عند التوسع
$metrics = [
// البنية التحتية
'cpu_usage_percent' => 70, // التشغيل: > 80%
'memory_usage_percent' => 65, // التشغيل: > 85%
'disk_usage_percent' => 50, // التشغيل: > 90%

// التطبيق
'requests_per_second' => 1200, // تخطيط السعة
'avg_response_time_ms' => 150, // التشغيل: > 500ms
'error_rate_percent' => 0.1, // التشغيل: > 1%

// قاعدة البيانات
'db_connections_used' => 45, // الحد الأقصى: 100
'query_time_p95_ms' => 200, // التشغيل: > 1000ms
'cache_hit_rate_percent' => 92, // الهدف: > 90%

// قائمة الانتظار
'queue_size' => 150, // التشغيل: > 1000
'queue_processing_time_ms' => 500,
];
أفضل الممارسات: ابدأ بالتوسع الرأسي للبساطة، انتقل إلى التوسع الأفقي قبل الوصول إلى حدود الأجهزة، صمم بدون حالة مبكراً، استخدم الخدمات المدارة (RDS، ElastiCache، SQS) لتقليل التعقيد التشغيلي، وقم دائماً باختبار الحمل قبل الإنتاج.
تمرين: صمم معمارية توسع لتطبيقك. حدد المكونات ذات الحالة وخطط لكيفية جعلها بدون حالة. قم بتكوين تخزين الجلسة في Redis. قم بإعداد موازن حمل بسيط مع Nginx لـ 2+ مثيل تطبيق. اختبر التوسع الأفقي عن طريق قياس تحسينات الأداء.