خلاصة سريعة: الحل القياسي لجعل مهام الضغط غير متزامنة هو قائمة انتظار المهام Celery + Redis، والبنية الأساسية هي: Producer (FastAPI لإرسال المهام) ← Broker (Redis للتخزين المؤقت) ← Worker (Celery للاستهلاك واستدعاء محرك ضغط Rust) ← Backend (Redis لتخزين النتائج). عند معالجة 10000 ملف دفعة واحدة، بتقسيم كل دفعة إلى 100 ملف و12 عامل Worker متزامن، يمكن إكمال كل شيء في نحو 40 دقيقة. نقاط التصميم الرئيسية هي تقسيم المهام، قوائم الانتظار ذات الأولوية، إعادة المحاولة مع التراجع الأسي، وقائمة الانتظار المهمّة (DLQ) كحل أخير. سنبدأ ببنية قائمة الانتظار، ثم نقدم جدول معاملات تهيئة Celery وخطة تنفيذ كاملة.
إذا لم تكن على دراية بالنشر الشامل لخدمة الضغط، يُنصح بقراءة الدليل الكامل لنشر خدمة الضغط عبر Docker.
أولًا: لماذا تحتاج مهام الضغط إلى قائمة انتظار غير متزامنة
يُعدّ ضغط الملفات مهمة نموذجية كثيفة الاستخدام للمعالج وكثيفة الإدخال/الإخراج في آن واحد. قد يستغرق ضغط ملف PDF بحجم 100 ميجابايت من 5 إلى 15 ثانية، وعند معالجته عبر واجهة متزامنة يظل اتصال HTTP معلقًا لفترة طويلة، وتكون قدرة التزامن على جهاز واحد ضعيفة للغاية. تفصل قائمة الانتظار غير المتزامنة بين «الإرسال» و«التنفيذ»: يحصل العميل فورًا على task_id بعد إرسال المهمة، ويضغطها الـ Worker في الخلفية ببطء، ثم تُعاد النتيجة عبر الاستدعاء أو الاستعلام الدوري بعد الاكتمال.
| طريقة المعالجة | قدرة التزامن | زمن الاستجابة | معالجة الفشل | الحجم المناسب |
|---|---|---|---|---|
| المعالجة المتزامنة | ضعيفة (تحجب اتصال HTTP) | 5–60 ثانية | بدون إعادة محاولة، خطأ مباشر | أقل من 10 ملفات |
| تجمع الخيوط | متوسطة (محدودة بعدد الخيوط) | 1–5 ثوانٍ | تحتاج تنفيذ يدوي | 10–100 ملف |
| قائمة Celery غير المتزامنة | عالية (توسعة أفقية للـ Worker) | أقل من 200 مللي ثانية | إعادة محاولة تلقائية + قائمة DLQ | 100–100000 ملف |
| Kubernetes + قائمة انتظار | عالية جدًا (توسعة مرنة HPA) | أقل من 100 مللي ثانية | نظام تحمّل أخطاء كامل | أكثر من 100000 ملف |
تعتمد الإصدارة الشبكية من SmartSlim لـ UGLYPEAR DATA بنية FastAPI + Celery + Redis + MinIO، وتعمل بشكل مستقر بـ 12 مهمة متزامنة على جهاز واحد، وعند نشرها على Kubernetes يمكن لـ HPA التوسع والانكماش تلقائيًا بين 3 و10 نسخ. تدعم هذه البنية بالفعل احتياجات الضغط اليومية بعشرات الآلاف من الملفات لعملاء من المؤسسات.
ثانيًا: شرح مفصّل لبنية قائمة انتظار Celery
تتكون قائمة انتظار المهام Celery من أربعة أدوار أساسية: Producer (المنتج)، Broker (وكيل الرسائل)، Worker (عملية العمل)، Backend (تخزين النتائج). إن فهم مسؤوليات هذه الطبقات الأربع هو الأساس لتهيئة قائمة انتظار مهام الضغط بشكل صحيح.
| المكوّن | الخيار التقني | المسؤولية | الإعداد الرئيسي |
|---|---|---|---|
| Producer | FastAPI | استقبال طلبات HTTP، إنشاء المهام، إرسالها إلى Broker | task.apply_async(queue=...) |
| Broker | Redis 7.x | تخزين رسائل المهام المعلّقة مؤقتًا، يدعم قوائم الأولوية | broker_url, visibility_timeout |
| Worker | Celery 5.x | استهلاك المهام، استدعاء محرك ضغط Rust لتنفيذ الضغط | concurrency, prefork pool |
| Backend | Redis | تخزين حالة المهام والنتائج المُعادة | result_backend, result_expires |
| طبقة التخزين | MinIO | تخزين الملفات الأصلية والملفات المضغوطة | بروتوكول متوافق مع S3، رفع مجزأ |
1. جدول معاملات تهيئة Celery الأساسية
تحدد إعدادات Celery بشكل مباشر قدرة واستقرار قائمة الانتظار. يعرض الجدول التالي الإعدادات الموصى بها لسيناريو الضغط، وقد تم التحقق منها في بيئة الإنتاج لـ SmartSlim.
| المعامل | القيمة الموصى بها | الوصف |
|---|---|---|
| broker_url | redis://:password@redis:6379/0 | Redis كوكيل رسائل، استخدام قاعدة بيانات مستقلة لتجنب التعارضات |
| result_backend | redis://:password@redis:6379/1 | تخزين النتائج في قاعدة بيانات مستقلة، معزولة عن Broker |
| task_serializer | json | تسلسل JSON، متوافق عبر اللغات |
| result_serializer | json | النتائج تستخدم JSON أيضًا |
| accept_content | ['json'] | قبول JSON فقط، تعزيز الأمان |
| timezone | Asia/Shanghai | توحيد المنطقة الزمنية |
| task_acks_late | True | إرسال ACK بعد اكتمال المهمة، حتى لا تضيع المهمة عند الانهيار |
| worker_prefetch_multiplier | 1 | كل Worker يجلب مهمة واحدة فقط مسبقًا، لتجنب تجويع المهام الطويلة |
| task_time_limit | 600 | مهلة صلبة 600 ثانية للمهمة الواحدة |
| task_soft_time_limit | 540 | مهلة لينة 540 ثانية، تُفعّل SoftTimeLimitExceeded |
| task_reject_on_worker_lost | True | رفض المهمة عند خروج Worker بشكل غير طبيعي، وإعادة إدخالها في القائمة |
| result_expires | 86400 | تنظيف النتائج تلقائيًا بعد 24 ساعة |
2. تصميم قائمة الانتظار حسب الأولوية
تتفاوت مهام الضغط في أولويتها: طلبات الضغط في الوقت الحقيقي من المستخدم تتطلب استجابة سريعة، أما مهام الأرشفة المجدولة فيمكن تشغيلها ببطء. تُنفَّذ جدولة تفاضلية عبر قائمة الأولوية في Redis، حيث تُستهلَك المهام ذات الأولوية العالية من قِبل Worker أولًا.
| اسم قائمة الانتظار | الأولوية | قاعدة التوجيه | المهام النمطية |
|---|---|---|---|
| compression_high | 9 (الأعلى) | طلبات المستخدم في الوقت الحقيقي | ضغط فوري لملف واحد |
| compression_normal | 5 (افتراضي) | المهام الدفعية | ضغط دفعي من 100 إلى 500 ملف |
| compression_low | 1 (الأدنى) | أرشفة مجدولة | أرشفة كاملة ليلية |
| dlq_queue | — (قائمة مهمّة) | المهام التي فشلت إعادة المحاولة فيها | معالجة يدوية أو تعويضية |
ثالثًا: حالة عملية: ضغط غير متزامن لـ 10000 ملف
هذه حالة أرشفة بيانات لمؤسسة ما: 10000 وثيقة تاريخية (مزيج من PDF وWord والصور، متوسط حجم الملف 8 ميجابايت، بإجمالي نحو 80 جيجابايت) تحتاج إلى ضغط وأرشفة موحَّدين. المتطلب: الاكتمال خلال ساعة واحدة، ونسبة ضغط لا تقل عن 60%.
تصميم الحل: تقسيم 100 ملف لكل دفعة إلى 100 مهمة فرعية، والإرسال بالدفعات عبر مجموعة Celery group، مع استهلاك 12 Worker بشكل متزامن. تستدعي كل مهمة فرعية محرك ضغط Rust بشكل تسلسلي لضغط 100 ملف.
معاملات التنفيذ وأداء الإنتاجية:
| المؤشر | المعامل | القيمة المقاسة | الوصف |
|---|---|---|---|
| إجمالي عدد الملفات | 10000 ملف | — | صيغ مختلطة، متوسط 8 ميجابايت/الملف |
| دقة التقسيم | 100 ملف/دفعة | 100 مهمة فرعية | موازنة بين تكلفة الجدولة وتكلفة إعادة المحاولة |
| تزامن الـ Worker | 12 | وضع prefork | معالج أحادي بـ 12 نواة |
| زمن ضغط الملف الواحد | — | متوسط 3.2 ثانية | محرك Rust بمستوى medium |
| زمن الدفعة الواحدة | — | نحو 5.3 دقيقة | 100 ملف بشكل تسلسلي |
| الزمن الإجمالي | — | نحو 44 دقيقة | 100 دفعة / 12 تزامن |
| نسبة الضغط | — | 67.3% | 80 جيجابايت ← 26.2 جيجابايت |
| عدد حالات الفشل | — | 17 ملفًا | ملفات تالفة، إلى قائمة DLQ بعد إعادة المحاولة |
النتيجة: إكمال ضغط 10000 ملف في 44 دقيقة بنسبة ضغط 67.3%، وانتقال 17 ملفًا تالفًا تلقائيًا إلى قائمة DLQ للمعالجة اليدوية. البنية الكلية مستقرة، وبلغت ذروة استخدام المعالج 89%، وبلغ استهلاك الذاكرة 4.2 جيجابايت، دون أي OOM أو فقدان للمهام.
3. استراتيجية إعادة المحاولة وقائمة الانتظار المهمّة
تُقسَّم أسباب فشل مهام الضغط إلى فئتين: أخطاء مؤقتة (انتهاء مهلة الإدخال/الإخراج، نقص الذاكرة، تزامن مفرط) وأخطاء حتمية (ملف تالف، صيغة غير مدعومة). الأخطاء المؤقتة تنجح إعادة المحاولة فيها باحتمال كبير، أما الأخطاء الحتمية فلا جدوى من إعادة المحاولة فيها. يعرض الجدول التالي استراتيجية القرار لإعادة المحاولة وقائمة DLQ.
| نوع الخطأ | الاستثناء النمطي | استراتيجية المعالجة | عدد مرات إعادة المحاولة | المصير النهائي |
|---|---|---|---|---|
| مؤقت - IO | ConnectionError, TimeoutError | إعادة المحاولة بتراجع أسي | 3 مرات | نجاح أو DLQ |
| مؤقت - موارد | MemoryError, OOMKilled | تمديد التراجع + تخفيض المستوى | مرتان | نجاح أو DLQ |
| حتمي - ملف | FileCorrupted, ParseError | دون إعادة محاولة، مباشرة إلى DLQ | 0 | dlq_queue |
| حتمي - صيغة | UnsupportedFormat | دون إعادة محاولة، مباشرة إلى DLQ | 0 | dlq_queue |
| حتمي - أذونات | PermissionDenied | دون إعادة محاولة، تنبيه | 0 | dlq_queue + تنبيه |
تستخدم إعدادات إعادة المحاولة معاملَي autoretry_for وretry_backoff في Celery، مع تراجع ابتدائي قدره 60 ثانية وأقصى 600 ثانية، إضافة إلى ارتجاع عشوائي لتجنب الانهيار الجماعي. تُفحَص المهام في قائمة DLQ دوريًا عبر مهمة مراقبة مستقلة، تُفعِّل تنبيهات WeChat للأعمال أو DingTalk لإعلام عمليات التشغيل بالمعالجة.
| مؤشر المراقبة | طريقة الجمع | حد التنبيه | إجراء المعالجة |
|---|---|---|---|
| حجم تراكم قائمة الانتظار | Redis LLEN | >500 | تفعيل توسيع Worker |
| معدل فشل المهام | أحداث Celery | >5% | فحص السجلات + إيقاف الإرسال |
| طول قائمة DLQ | Redis LLEN dlq | >10 | تنبيه WeChat للأعمال |
| عدد الـ Worker النشطة | Celery inspect | <10 | إعادة تشغيل Worker تلقائيًا |
| متوسط زمن المهمة | مراقبة Flower | >30 ثانية | فحص الملفات الكبيرة + تخفيض المستوى |
| استخدام المعالج | node_exporter | >95% | تحديد المعدل + التوسيع |
بخصوص طريقة الاستدعاء الكاملة لواجهة برمجة الضغط، يمكنك الرجوع إلى دليل استدعاء واجهة برمجة الضغط: تصميم واجهة REST.
رابعًا: توصيات تهيئة قائمة الانتظار حسب السيناريو
تتفاوت متطلبات الإنتاجية والاستجابة والموثوقية باختلاف سيناريوهات الأعمال، ويجب تخصيص تهيئة قائمة الانتظار تبعًا لذلك. يعرض الجدول التالي الإعدادات الموصى بها للسيناريوهات الشائعة.
| السيناريو | عدد Workers | دقة التقسيم | قائمة الأولوية | استراتيجية إعادة المحاولة |
|---|---|---|---|---|
| ضغط فردي فوري | 2 | بدون تقسيم | high | إعادة محاولة سريعة 3 مرات |
| أرشفة مؤسسية دفعية | 12 | 100 ملف/دفعة | normal/low | تراجع أسي 3 مرات |
| معالجة حكومية سرية | 4 | 50 ملف/دفعة | high | إعادة محاولة صارمة + تدقيق |
| صور منصات التجارة الإلكترونية | 16 | 200 ملف/دفعة | normal | إعادة محاولة سريعة مرتين |
| أرشفة ليلية مجدولة | 8 | 500 ملف/دفعة | low | تراجع بطيء 5 مرات |
| تحويل فيديو في الوقت الحقيقي | 24 | ملف واحد | high | دون إعادة محاولة، تنبيه عند الفشل |
مبدأ عام: السيناريوهات الفورية تستخدم قائمة أولوية عالية + تقسيم صغير + إعادة محاولة سريعة، والسيناريوهات الدفعية تستخدم قائمة عادية + تقسيم كبير + تراجع أسي، والسيناريوهات السرية تستخدم تدقيقًا صارمًا + تقسيمًا صغيرًا + إعادة محاولة متعددة المستويات. يمكن الرجوع إلى الخطة الكاملة للضغط الدفعي على مستوى المؤسسات في حل الضغط الدفعي للمؤسسات: تطبيق عملي لمعالجة عشرات الآلاف من الملفات.
خامسًا: الأسئلة الشائعة FAQ
س1: كيف تنفّذ مهام ضغط Celery المعالجة غير المتزامنة؟
أنشئ قائمة انتظار مهام غير متزامنة عبر Celery + Redis: يستقبل FastAPI الطلب ثم يُرسل المهمة إلى Redis Broker، ويستهلك Celery Worker المهمة من Broker ويستدعي محرك ضغط Rust لتنفيذ الضغط، وتُكتب النتائج في Backend وتُخزَّن في MinIO. يكفي استدعاء task.apply_async لتنفيذ المهمة بشكل غير متزامن، والاستعلام عن الحالة عبر task.id. مع 12 Worker متزامن على جهاز واحد، يمكن إكمال 10000 ملف في 100 دفعة خلال 40 دقيقة.
س2: كيف تُعيد مهام الضغط الفاشلة المحاولة تلقائيًا؟
اضبط إعادة المحاولة التلقائية عبر معامل autoretry_for في Celery، مع max_retries=3، وretry_backoff=True (تراجع أسي، ابتدائي 60 ثانية)، وretry_backoff_max=600 ثانية، وretry_jitter=True (ارتجاع عشوائي لتجنب الانهيار الجماعي). تُرسَل المهام التي تفشل بعد 3 محاولات تلقائيًا إلى قائمة DLQ dlq_queue للمعالجة اليدوية أو التعويضية. يُنصح بإعادة المحاولة للأخطاء المؤقتة (انتهاء مهلة IO/نقص الذاكرة)، أما الأخطاء الحتمية (ملف تالف/صيغة غير مدعومة) فإلى DLQ مباشرة.
س3: كيف تُقسَّم 10000 ملف دفعيًا للضغط؟
قسّم إلى 100 ملف لكل دفعة بإجمالي 100 مهمة فرعية. أرسل بالدفعات عبر Celery group أو chord، واستهلك بالتوازي عبر 12 Worker، وكل مهمة فرعية تضغط 100 ملف تسلسليًا. متوسط زمن ضغط الملف 3 ثوانٍ، ووقت الدفعة نحو 5 دقائق، وبعد تشغيل 100 دفعة بالتوازي يكتمل الإجمالي في نحو 40 دقيقة. التقسيم الصغير جدًا (مثل 1 لكل دفعة) يرفع تكلفة الجدولة، والتقسيم الكبير جدًا (مثل 1000 لكل دفعة) يرفع تكلفة إعادة المحاولة عند الفشل، و100 هي القيمة المثلى تجريبيًا.
س4: أيهما أنسب لقائمة انتظار مهام الضغط: Celery أم RQ؟
يُنصح بـ Celery لمهام الضغط. يدعم Celery تقسيم المهام (group/chord)، وقوائم الأولوية، والمهام المجدولة، وسلاسل المهام، وقوائم DLQ، بمجموعة ميزات متكاملة؛ أما RQ فأخفّ لكنه يفتقر إلى التقسيم والأولوية. يدعم Celery بشكل أصلي احتياجات سيناريو الضغط الشائعة: التقسيم الدفعي، الجدولة حسب الأولوية، إعادة المحاولة عند الفشل. من حيث الأداء يعتمد كلاهما على Redis، والإنتاجية متقاربة. تعتمد الإصدارة الشبكية من SmartSlim لـ UGLYPEAR DATA بنية FastAPI + Celery + Redis + MinIO، وتعمل بشكل مستقر بـ 12 تزامن على جهاز واحد.
الخلاصة
الحل القياسي لجعل مهام الضغط غير متزامنة هو قائمة انتظار Celery + Redis، وجوهرها يكمن في الفصل بين الطبقات الأربع Producer/Broker/Worker/Backend. عند مواجهة دفعات بعشرات الآلاف من الملفات، يكفي تقسيمها إلى 100 ملف لكل دفعة و12 Worker متزامن لإكمالها في نحو 40 دقيقة بنسبة ضغط 60%–70%. يجب أن تميّز استراتيجية إعادة المحاولة بين الأخطاء المؤقتة (إعادة المحاولة بتراجع أسي) والأخطاء الحتمية (مباشرة إلى DLQ)، مدعومة بستة مؤشرات مراقبة لضمان استقرار قائمة الانتظار.
احفظ ثلاث نقاط: أولًا، task_acks_late=True يضمن عدم فقدان المهام عند الانهيار؛ ثانيًا، worker_prefetch_multiplier=1 يتجنب تجويع المهام الطويلة؛ ثالثًا، يجب ربط قائمة DLQ بمراقبة وتنبيهات. اختيار بنية قائمة الانتظار الصحيحة واستراتيجية التقسيم الملائمة يرفع إنتاجية واستقرار خدمة الضغط إلى مستوى جديد.
مقالات ذات صلة
هل تحتاج إلى ضغط الملفات؟ جرّب SmartSlim من UGLYPEAR DATA
مبني على محرك ضغط Rust ذاتي التطوير، يدعم 10 فئات رئيسية بأكثر من 40 صيغة تشمل PDF والصور والفيديو وOffice وOFD، مع ضغط محلي لا تغادر فيه البيانات نطاقك.