404 Not Found

404 Not Found


nginx

تحسين الأداء — من 100 إلى مليون QPS

تحسين الأداء مثل بناء طريق سريع — تبدأ بإصلاح أكثر الأقسام ازدحامًا (العنق الزجاجي)، وتقيس النتائج بعد إكمال كل قسم، بدلاً من توسيع الطريق بأكمله بشكل عشوائي. التحسين الأعمى إهدار؛ التحسين القائم على القياس هو النهج الأكثر كفاءة.

1. ما ستتعلمه


2. القصة الحقيقية لـ Alice

(1) نقطة الألم: 500 QPS على خادم واحد غير كافية

بعد إطلاق PriceTracker، لم يتمكن خادم واحد من التعامل سوى مع 500 QPS، لكن خلال أحداث التخفيضات الكبرى، ارتفعت حركة المرور إلى 5,000 QPS، مما تسبب في انتهاء مهل استجابة API. أضاف Charlie خوادم إضافية، لكن التحسن كان ضئيلاً — نُفد تجمع اتصالات قاعدة البيانات، والكود المتزامن كان يحظر حلقة الأحداث، وتسلسل JSON كان يستهلك 40% من المعالج.

(2) نهج منهجي لتحسين الأداء

تحسين الأداء ليس تخمينًا؛ إنه دورة من القياس ← تحديد العنق الزجاجي ← التحسين ← التحقق. نعالج المشاكل طبقة تلو الأخرى، من مستوى الكود (async/التسلسل) إلى مستوى قاعدة البيانات (الفهارس/تجمعات الاتصال) إلى المستوى المعماري (Workers/موازنة التحميل).

(3) العائد

تم تحسين QPS لـ PriceTracker من 500 إلى 10,000 (على خادم واحد)، ومع مجموعة موازنة تحميل، يمكن الوصول إلى مليون QPS، بينما انخفض زمن الاستجابة P99 من 500 مللي ثانية إلى 30 مللي ثانية.


3. مستويات تحسين الأداء

(1) نموذج التحسين ثلاثي الطبقات

100%
graph TD
    L3[طبقة البنية التحتية] --> L2[طبقة قاعدة البيانات]
    L2 --> L1[طبقة التطبيق]
    
    L1 --- A1[async/await - كشف الحظر]
    L1 --- A2[التسلسل - orjson]
    L1 --- A3[الضغط - GZip]
    
    L2 --- D1[الفهارس - تحسين الاستعلامات]
    L2 --- D2[تجمع الاتصالات - ضبط الحجم]
    L2 --- D3[أنماط الاستعلام - منع N+1]
    
    L3 --- I1[Workers - إعداد Gunicorn]
    L3 --- I2[موازن التحميل - Nginx]
    L3 --- I3[التوسع التلقائي - K8s HPA]

(2) مسارات تحسين QPS

100%
flowchart LR
    A[500 QPS\nخط الأساس] -->|async + orjson| B[2000 QPS]
    B -->|فهارس قاعدة البيانات + التجمع| C[10000 QPS]
    C -->|4 Workers| D[40000 QPS]
    D -->|Nginx LB x 5| E[200000 QPS]
    E -->|K8s x 5 pods| F[1000000 QPS]
المرحلة طريقة التحسين QPS عامل التحسين
خط الأساس الإعداد الافتراضي 500 1x
طبقة التطبيق async + orjson 2,000 4x
طبقة قاعدة البيانات فهارس + تجمع اتصالات 10,000 20x
Workers 4 Workers + Gunicorn 40,000 80x
موازنة التحميل 5 نسخ Nginx 200,000 400x
مرونة K8s 5 Pods توسع تلقائي 1,000,000 2000x

4. تحسين طبقة التطبيق

(1) استكشاف الحظر غير المتزامن

(1) ▶مثال: كود متزامن يحظر حلقة الأحداث

PYTHON
import asyncio
import time

# سيء: الإدخال/الإخراج المتزامن يحظر حلقة الأحداث
async def get_price_bad(product_id: int):
    time.sleep(0.1)  # يحظر جميع الطلبات الأخرى لمدة 100 مللي ثانية!
    return {"product_id": product_id, "price": 9.99}

# جيد: استخدم asyncio للإدخال/الإخراج
async def get_price_good(product_id: int):
    await asyncio.sleep(0.1)  # غير حظر، تستمر الطلبات الأخرى
    return {"product_id": product_id, "price": 9.99}

الناتج:

TEXT
# تم تعريف الدالة بنجاح

(2) ▶مثال: run_in_executor يُغلف الكود المتزامن

PYTHON
import asyncio
from functools import partial

# دالة متزامنة لا يمكن تحويلها إلى غير متزامنة
def sync_scrape_price(url: str) -> float:
    # تستخدم مكتبة requests (متزامنة فقط)
    import requests
    response = requests.get(url, timeout=10)
    return parse_price(response.text)

async def get_price_async(product_id: int):
    loop = asyncio.get_event_loop()
    # تشغيل الدالة المتزامنة في تجمع الخيوط - لا تحظر حلقة الأحداث
    price = await loop.run_in_executor(
        None,  # تجمع الخيوط الافتراضي
        partial(sync_scrape_price, f"https://api.example.com/prices/{product_id}"),
    )
    return {"product_id": product_id, "price": price}

الناتج:

TEXT
# تم تعريف الدالة بنجاح

(2) تحسين تسلسل JSON

(3) ▶مثال: استخدام orjson بدلاً من json

PYTHON
# تثبيت: uv add orjson
from fastapi import FastAPI
from fastapi.responses import ORJSONResponse

app = FastAPI(default_response_class=ORJSONResponse)

@app.get("/api/v1/products")
async def list_products():
    # orjson أسرع 3-10 مرات من stdlib json
    return {"products": [{"id": i, "name": f"Product {i}"} for i in range(100)]}

الناتج:

TEXT
# تم تعريف الدالة بنجاح
مكتبة التسلسل السرعة الوصف
json (المكتبة القياسية) مرجع القياس تنفيذ Pure Python
orjson 3-10x منفذ بـ Rust، يتعامل تلقائيًا مع datetime
ujson 2-3x تنفيذ C، بعض مشاكل التوافق

(4) ▶مثال: ضغط GZipMiddleware

PYTHON
from fastapi import FastAPI
from starlette.middleware.gzip import GZipMiddleware

app = FastAPI()
app.add_middleware(GZipMiddleware, minimum_size=1000)  # ضغط الاستجابات الأكبر من 1 كيلوبايت

@app.get("/api/v1/products")
async def list_products():
    # يتم ضغط استجابات JSON الكبيرة تلقائيًا
    return {"products": [...]}  # 50 كيلوبايت ← ~5 كيلوبايت مع gzip

الناتج:

TEXT
# تم تعريف الدالة بنجاح

5. تحسين طبقة قاعدة البيانات

(1) استراتيجية الفهرسة

(1) ▶مثال: تصميم فهارس PriceTracker

PYTHON
from sqlalchemy import Index

class Product(Base):
    __tablename__ = "products"
    __table_args__ = (
        # فهارس عمود واحد للفلاتر الشائعة
        Index("ix_products_category", "category"),
        Index("ix_products_created_at", "created_at"),
        # فهرس مركب لاستعلامات الفئة + نطاق السعر
        Index("ix_products_category_base_price", "category", "base_price"),
        # فهرس جزئي للمنتجات النشطة فقط (خاص بـ PostgreSQL)
        Index("ix_products_active", "created_at", postgresql_where=("deleted_at IS NULL")),
    )
    ...

الناتج:

TEXT
# التنفيذ ناجح
نوع الفهرس الاستعلامات المناسبة مثال
فهرس عمود واحد فلترة مساواة/نطاق WHERE category = ?
فهرس مركب تركيبات شروط متعددة WHERE category = ? AND price > ?
فهرس جزئي مجموعة فرعية مشروطة WHERE deleted_at IS NULL
فهرس تغطي تجنب البحث في الجدول INCLUDE (name, price)

(2) ضبط تجمع الاتصالات

(2) ▶مثال: إعداد تجمع اتصالات بمستوى الإنتاج

PYTHON
engine = create_async_engine(
    DATABASE_URL,
    pool_size=25,           # اتصالات دائمة لكل Worker
    max_overflow=10,        # اتصالات إضافية عند نفاد التجمع
    pool_timeout=30,        # وقت الانتظار للحصول على اتصال متاح
    pool_recycle=1800,      # إعادة تدوير الاتصالات بعد 30 دقيقة
    pool_pre_ping=True,     # فحص الاتصال قبل الاستخدام
    echo=False,             # تعطيل تسجيل SQL في الإنتاج
)

الناتج:

TEXT
# التنفيذ ناجح
المعامل القيمة الموصى بها الوصف
pool_size أنوية المعالج x 2 + 1 عدد الاتصالات الأساسي
max_overflow pool_size x 0.5 أقصى عدد اتصالات الانفجار
pool_timeout 30 ثانية مهلة الانتظار
pool_recycle 1800 ثانية منع MySQL/PG من إغلاق الاتصالات الخاملة
pool_pre_ping True منع استخدام الاتصالات المقطوعة

6. تحسين نموذج التزامن

(1) إعداد Workers

(1) ▶مثال: Gunicorn + Uvicorn Workers

BASH
# الإنتاج: يدير Gunicorn عمال Uvicorn
gunicorn app.main:app \
    --workers 9 \
    --worker-class uvicorn.workers.UvicornWorker \
    --bind 0.0.0.0:8000 \
    --timeout 120 \
    --graceful-timeout 30 \
    --access-logfile - \
    --error-logfile -

الناتج:

TEXT
# تم تنفيذ الأمر بنجاح
DOCKERFILE
# في Dockerfile
CMD ["gunicorn", "app.main:app", \
     "--workers", "9", \
     "--worker-class", "uvicorn.workers.UvicornWorker", \
     "--bind", "0.0.0.0:8000"]
الإعداد المعادلة/القيمة الوصف
عدد Workers (2 x CPU) + 1 4 أنوية = 9 عمال
worker-class UvicornWorker عامل ASGI
timeout 120 ثانية إنهاء Worker بسبب انتهاء المهلة
graceful-timeout 30 ثانية مهلة الإيقاف السلس
max-requests 10,000 إعادة تشغيل تلقائية لمنع تسرب الذاكرة

❓أسئلة شائعة

س كيف أحدد عنق الزجاجية في الأداء؟
ج استخدم سلسلة أدوات — cProfile لتحليل النقاط الساخنة في المعالج، py-spy لأخذ العينات في الوقت الفعلي، Prometheus لمراقبة توزيع زمن الاستجابة، وسجلات الاستعلامات البطيئة لتحليل قاعدة البيانات. قاس أولاً، ثم حسّن.
س ما حجم تجمع الخيوط لـ run_in_executor؟
ج الافتراضي هو min(32, os.cpu_count() + 4). للمهام كثيفة المعالج، استخدم ProcessPoolExecutor لتجنب قيود GIL.
س هل orjson متوافق مع Pydantic؟
ج نعم، متوافق. default_response_class=ORJSONResponse في FastAPI يُسلسل نماذج Pydantic تلقائيًا باستخدام orjson لإنتاج ناتج model_dump().
س كيف أوازن بين تجمع الاتصالات وعدد Workers؟
ج كل Worker لديه تجمع اتصالاته الخاص. 9 عمال x 25 اتصال = 225 اتصال قاعدة بيانات. الإعداد الافتراضي max_connections في PostgreSQL هو 100؛ ستحتاج إلى زيادة أو تقليل pool_size وفقًا لذلك.
س هل لضغط GZip تكلفة أداء؟
ج الضغط يستهلك موارد المعالج، لكنه يقلل وقت النقل عبر الشبكة. لاستجابات JSON الأكبر من 1 كيلوبايت، فوائد الضغط تفوق تكلفة المعالج بكثير (نسبة الضغط عادة 5 إلى 10 مرات).
س كيف أتحقق من فعالية التحسين؟
ج استخدم Locust أو k6 لإجراء اختبارات الحمل، وقارن QPS وزمن الاستجابة P50/P95/P99 واستخدام المعالج/الذاكرة قبل وبعد التحسين. غيّر متغيرًا واحدًا فقط في كل مرة.

📖ملخص


📝تمارين

  1. تمرين أساسي (الصعوبة: ⭐): ثبّت orjson، غيّر فئة الاستجابة الافتراضية في FastAPI إلى ORJSONResponse، واستخدم curl لمقارنة أوقات الاستجابة لـ JSON و orjson. تلميح: default_response_class=ORJSONResponse
  2. تمرين متقدم (الصعوبة ⭐⭐): أضف فهارس إلى جدول products في PriceTracker (category و created_at وفهرس مركب من category و base_price)، وقارن أوقات تنفيذ نفس الاستعلام قبل وبعد إضافة الفهارس. تلميح: Index("ix_name", "col1", "col2") + EXPLAIN ANALYZE
  3. تحدٍ (الصعوبة: ⭐⭐⭐): تحسين شامل للأداء — تسلسل orjson + GZipMiddleware + ضبط تجمع الاتصالات + Gunicorn مع 4 عمال. استخدم Locust لإجراء اختبارات الحمل وقارن QPS وزمن الاستجابة P99 قبل وبعد التحسين. تلميح: locust -f locustfile.py --host=http://localhost:8000

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%