
Chaos Monkey ولكن لاختبار الصوت والفيديو (webRTC وUDP)
منصة هندسة فوضى موزعة لاختبار تحميل أنظمة مؤتمرات الفيديو. تحاكي أكثر من 1500 مشارك WebRTC مع بث H.264/Opus وتحقن فوضى شبكية لاختبار مرونة النظام في ظل الظروف المتدهورة
خط أنابيب معالجة الوسائط:
مستوى التحكم:
مجموعة المشاركين:
participant_id % total_partitions = partition_idالتكوين التلقائي في Kubernetes:
orchestrator-3 ← PARTITION_ID=3base_port + (partition_id × 10000) + participant_indexسلسلة ترحيل UDP (Kubernetes فقط):
Orchestrator Pods (10×) ← UDP :5000 → udp-relay Pod (Python)
← TCP :5001 ببادئة طول ← kubectl port-forward 15001:5001
← tools/udp-relay (Go) ← UDP :5002 ← جهاز المستقبل لديك
البنية التحتية لـ WebRTC:
تكامل العميل:
مكدس المراقبة (اختياري):
/metrics من جميع حجرات الأوركستراتور كل 5 ثوانٍprometheus.io/scrape: "true"كل مشارك افتراضي يولد تيارات وسائط حقيقية:
خمسة أنواع من النبضات تحاكي ظروف الشبكة الواقعية:
يتم توزيع النبضات على مدى مدة الاختبار باستخدام استراتيجيات قابلة للتكوين:
تستخدم عمليات نشر Kubernetes تقسيم المشاركين للتوسع الأفقي:
participant_id % total_partitions == partition_idbase_port + (partition_id * 10000) + participant_indexالأفضل لـ: التطوير، التصحيح، اختبارات صغيرة النطاق (1-100 مشارك)
# بدء الأوركستراتور
go run cmd/main.go
# في طرفية أخرى: بدء مستقبل UDP
go run examples/go/udp_receiver.go 5002
# تعديل config/config.json لتعيين num_participants: 10
# تشغيل اختبار الفوضى
go run tools/chaos-test/main.go -config config/config.json
ماذا يحدث:
:8080127.0.0.1:5002التكوين (config/config.json):
{
"base_url": "http://localhost:8080",
"media_path": "public/rick-roll.mp4",
"num_participants": 10,
"duration_seconds": 300,
"spikes": {
"count": 20,
"interval_seconds": 5,
"types": { "rtp_packet_loss": {...}, "network_jitter": {...} }
},
"spike_distribution": {
"strategy": "random",
"min_spacing_seconds": 5,
"jitter_percent": 15
}
}
الأفضل لـ: الاختبار المعزول، CI/CD، اختبارات متوسطة النطاق (100-500 مشارك)
المتطلبات الأساسية:
docker-compose# بناء وبدء حاوية الأوركستراتور
./scripts/start_everything.sh build
# في طرفية أخرى: بدء مستقبل UDP
go run examples/go/udp_receiver.go 5002
# تعديل config/config.json لتعيين num_participants: 100
# تشغيل اختبار الفوضى (يستهدف الحاوية)
go run tools/chaos-test/main.go -config config/config.json
حدود الموارد (تعديل docker-compose.yaml):
services:
orchestrator:
deploy:
resources:
limits:
cpus: "14.0"
memory: 6G # زيادة لمزيد من المشاركين
دليل التوسع:
| ذاكرة Docker | الحد الأقصى للمشاركين | أنوية وحدة المعالجة المركزية |
|---|---|---|
| 8 جيجابايت | ~100 | 4 |
| 16 جيجابايت | ~250 | 8 |
| 24 جيجابايت | ~400 | 12 |
| 32 جيجابايت | ~500 | 14 |
الأفضل لـ: اختبارات واسعة النطاق (500-1500 مشارك)، التوسع الأفقي، التحقق من الإنتاج
المتطلبات الأساسية:
# Nix يوفر: Go، Docker، kubectl، kind، ffmpeg
nix develop
# أو استخدم direnv للتفعيل التلقائي
echo "use flake" > .envrc
direnv allow
# النشر التلقائي مع الإعدادات المثلى (يكشف موارد النظام)
./scripts/start_everything.sh run -config config/config.json
# أو تحديد ملفات وسائط مخصصة
./scripts/start_everything.sh run --media=path/to/video.mp4 -config config/config.json
ماذا يحدث:
kubectl port-forward لترحيل UDPالخيار A: مستقبل UDP (موصى به لـ Kubernetes)
# استقبال التيار المجمع من جميع المشاركين الـ 1500
go run ./examples/go/udp_receiver.go 5002
الخيار B: مستقبل WebRTC (مشاركين متعددين)
# الاتصال بما يصل إلى 150 مشاركًا عبر WebRTC
go run ./examples/go/webrtc_receiver.go http://localhost:8080 <test_id> 150
تدفق العمارة:
1500 مشارك عبر 10 حجرات
← كل حجرة: 150 مشاركًا
← القسم حسب participant_id % 10
← جميعهم يرسلون UDP إلى udp-relay:5000
← ترحيل UDP يجمع ← TCP :5001
← kubectl port-forward 15001:5001
← الترحيل المحلي يحول TCP ← UDP :5002
← مستقبلك يحصل على جميع التيارات الـ 1500
ملاحظة: يقوم البرنامج النصي start_everything.sh تلقائيًا بإعداد:
# بناء وتحميل الصورة
docker build -t chaos-monkey-orchestrator:latest .
kind load docker-image chaos-monkey-orchestrator:latest
# النشر
kubectl apply -f k8s/orchestrator/orchestrator.yaml
kubectl apply -f k8s/udp-relay/udp-relay.yaml
# انتظار الحجرات
kubectl wait --for=condition=ready pod -l app=orchestrator --timeout=300s
# إعادة توجيه المنفذ لترحيل UDP
kubectl port-forward udp-relay 15001:5001 &
# بدء الترحيل المحلي TCP→UDP
go run tools/udp-relay/main.go &
# في طرفية أخرى: بدء المستقبل
go run ./examples/go/udp_receiver.go 5002
# في طرفية أخرى: تشغيل اختبار الفوضى
go run tools/chaos-test/main.go -config config/config.json
# حذف موارد Kubernetes
./scripts/cleanup.sh
# أو حذف المجموعة بأكملها
kind delete cluster --name av-chaos-monkey
# بناء لـ Linux x86_64 (الأكثر شيوعًا)
nix build .#packages.x86_64-linux.av-chaos-monkey
# بناء لـ ARM64 (Raspberry Pi، AWS Graviton)
nix build .#packages.aarch64-linux.av-chaos-monkey
# بناء لـ macOS Intel
nix build .#packages.x86_64-darwin.av-chaos-monkey
# بناء لـ macOS Apple Silicon
nix build .#packages.aarch64-darwin.av-chaos-monkey
# موقع الملف الثنائي
./result/bin/main
# إنشاء اختبار
POST /api/v1/test/create
{
"test_id": "optional_id",
"num_participants": 100,
"video": {...},
"audio": {...},
"duration_seconds": 600,
"spikes": [...],
"spike_distribution": {
"strategy": "even",
"min_spacing_seconds": 5,
"jitter_percent": 15
}
}
# بدء الاختبار
POST /api/v1/test/{test_id}/start
# الحصول على المقاييس
GET /api/v1/test/{test_id}/metrics
# إيقاف الاختبار
POST /api/v1/test/{test_id}/stop
# الحصول على عرض SDP
GET /api/v1/test/{test_id}/sdp/{participant_id}
# تعيين إجابة SDP
POST /api/v1/test/{test_id}/sdp/{participant_id}
{"sdp_answer": "v=0..."}
# حقن نبضة
POST /api/v1/test/{test_id}/spike
{
"spike_id": "unique_id",
"type": "rtp_packet_loss",
"duration_seconds": 30,
"participant_ids": [1001, 1002],
"params": {"loss_percentage": "15"}
}
| النوع | المعلمات | التأثير |
|---|---|---|
rtp_packet_loss | loss_percentage (0-100) | إسقاط الحزم على طبقة RTP |
network_jitter | base_latency_ms, jitter_std_dev_ms | إضافة تباين في زمن الوصول |
bitrate_reduce | new_bitrate_kbps | خنق تشفير الفيديو |
frame_drop | drop_percentage (0-100) | تخطي إطارات الفيديو |
bandwidth_limit | bandwidth_kbps | سقف الإنتاجية الإجمالية |
{
"spike_distribution": {
"strategy": "even",
"min_spacing_seconds": 5,
"jitter_percent": 15,
"respect_min_offset": true
}
}
# المستقبل المقدم مع تحليل RTP
go run examples/go/udp_receiver.go 5002
المخرجات:
الاستماع لحزم RTP على منفذ UDP 0.0.0.0:5002
الحزمة #100 من 127.0.0.1:xxxxx:
معرف المشارك: 1001
نوع الحمولة: 96 (فيديو H.264)
التسلسل: 1234
الطابع الزمني: 90000
SSRC: 1001000
حجم الحمولة: 1200 بايت
═══════════════════════════════════════════════════════════
إحصائيات الحزمة
═══════════════════════════════════════════════════════════
المدة: 60 ثانية
إجمالي الحزم: 180000 (3000 حزمة/ثانية)
إجمالي البايتات: 450 ميجابايت (60 ميجابت/ثانية)
تفصيل حسب نوع الوسائط:
الفيديو (H.264): 120000 حزمة (66.7%)
الصوت (Opus): 60000 حزمة (33.3%)
التيارات الفريدة (SSRC): 1500
المشاركون الفريدون: 1500
# مشارك واحد
go run ./examples/go/webrtc_receiver.go http://localhost:8080 <test_id>
# مشاركون متعددون (حتى 150)
go run ./examples/go/webrtc_receiver.go http://localhost:8080 <test_id> 150
# مثال مع معرف اختبار فعلي
go run ./examples/go/webrtc_receiver.go http://localhost:8080 chaos_test_1770831684 150
ملاحظة: يتطلب WebRTC اتصالات 1:1. بالنسبة لـ Kubernetes، استخدم مستقبل UDP الذي يجمع جميع المشاركين تلقائيًا.
تنسيق حزمة RTP:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Extension ID=1 | Length=4 | Participant ID (uint32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| H.264/Opus Payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
أنواع الحمولة:
96: فيديو H.264 (RFC 6184)111: صوت Opus (RFC 7587)استخراج معرف المشارك:
// هل تم تعيين بت الإضافة؟
if (packet[0] & 0x10) != 0 {
offset := 12 + int(packet[0]&0x0F)*4 // تخطي CSRC
extID := binary.BigEndian.Uint16(packet[offset:])
if extID == 1 {
participantID := binary.LittleEndian.Uint32(packet[offset+4:])
}
}
| المشاركون | الذاكرة | وحدة المعالجة المركزية | النطاق الترددي |
|---|---|---|---|
| 100 | 2 جيجابايت | 2 نوى | 250 ميجابت/ثانية |
| 500 | 6 جيجابايت | 8 نوى | 1.2 جيجابت/ثانية |
| 1000 | 12 جيجابايت | 16 نواة | 2.5 جيجابت/ثانية |
| 1500 | 18 جيجابايت | 24 نواة | 3.7 جيجابت/ثانية |
لكل مشارك (1280x720@30fps + Opus):
# مكشوفة على نقطة نهاية /metrics
av_chaos_monkey_participants_total
av_chaos_monkey_packets_sent_total
av_chaos_monkey_bytes_sent_total
av_chaos_monkey_spikes_active
av_chaos_monkey_packet_loss_percent
av_chaos_monkey_jitter_ms
# وضع Docker: بدء مكدس المراقبة
docker-compose --profile monitoring up
# وضع Kubernetes: نشر المراقبة
kubectl apply -f k8s/monitoring/prometheus-rbac.yaml
kubectl apply -f k8s/monitoring/prometheus.yaml
kubectl apply -f k8s/monitoring/grafana.yaml
# الوصول إلى Grafana
# Docker: http://localhost:3000
# Kubernetes: http://localhost:30030 (NodePort)
# بيانات الاعتماد الافتراضية: admin/admin
# الوصول إلى Prometheus
# Docker: http://localhost:9091
# Kubernetes: http://localhost:30090 (NodePort)
الاكتشاف التلقائي في Kubernetes:
prometheus.io/scrape: "true"/metrics من جميع الحجرات كل 5 ثوانٍ# الحصول على مقاييس الاختبار
curl http://localhost:8080/api/v1/test/{test_id}/metrics | jq
# المخرجات
{
"aggregate": {
"total_frames_sent": 45000,
"total_packets_sent": 180000,
"total_bitrate_kbps": 250000,
"avg_jitter_ms": 12.5,
"avg_packet_loss": 2.3,
"avg_mos_score": 4.1
}
}
# التحقق من تكوين هدف UDP
kubectl logs orchestrator-0 | grep "UDP transmission enabled"
# التحقق من تشغيل ترحيل UDP
kubectl get pod udp-relay
# التحقق من إعادة توجيه المنفذ
ps aux | grep "kubectl port-forward"
# اختبار اتصال UDP
nc -u -z localhost 5002
# التحقق من خادم TURN
kubectl get svc coturn-lb
# التحقق من مرشحي ICE
kubectl logs orchestrator-0 | grep "ICE"
# اختبار اتصال TURN
turnutils_uclient -v -u webrtc -w webrtc123 <turn-server>:3478
# التحقق من عدد المشاركين لكل حجرة
kubectl exec orchestrator-0 -- curl -s http://localhost:8080/api/v1/test/{test_id}/metrics | jq '.participants | length'
# تقليل المشاركين أو زيادة عدد الحجرات
go run tools/k8s-start/main.go -replicas 10 -participants 1000
# زيادة ذاكرة Docker (Docker Desktop)
# الإعدادات ← الموارد ← الذاكرة ← 16 جيجابايت
لا يمكن لمقبس UDP واحد التعامل مع أكثر من 3000 تيار متزامن دون تجاوز سعة المخزن المؤقت للنواة. الحلول:
setsockopt(SO_RCVBUF, 8MB)رخصة BSD 3-Clause
المساهمات مرحب بها! المجالات الرئيسية: