مرحباً بالجميع،
أنا أقوم بتشغيل منصة n8n متعددة المستأجرين حيث يتصل كل مستأجر بواجهات برمجية خارجية بحدود معدل مختلفة.
التحدي هو أن بعض المستأجرين لديهم حدود منخفضة جداً، بينما يمتلك آخرون حصصاً أعلى بكثير.
الإعداد الحالي: Webhook → Queue → Worker → External API
المشاكل التي أراها:
• بعض المستأجرين يصلون إلى حدود المعدل بسرعة أكبر من الآخرين
• إعادة المحاولات تخلق انفجارات حركة مرور
• يمكن لمستأجر واحد مشغول أن يستهلك جزءاً كبيراً من سعة العامل
• من الصعب فرض الاستخدام العادل عبر المستأجرين
أنا أفكر في:
• طوابير خاصة بكل مستأجر
• حد معدل دلو الرموز / دلو متسرب
• عدادات Redis
• عمال مخصصين للمستأجرين عالي الحجم
للفرق التي تقوم بتشغيل الأتمتة متعددة المستأجرين على نطاق واسع:
• كيف تفرضون حدود المعدل لكل مستأجر؟
• هل تعزلون الطوابير حسب المستأجر أم تستخدمون طابور مشترك مع الخنق؟
• أي أنماط موصى بها لمنع مستأجر واحد من التأثير على الآخرين مع الحفاظ على إنتاجية جيدة؟
وصف المشكلة/الخطأ/السؤال
ما رسالة الخطأ (إن وجدت)؟
يرجى مشاركة سير العمل الخاص بك
(حدد العُقد على لوحتك واستخدم اختصارات لوحة المفاتيح CMD+C/CTRL+C و CMD+V/CTRL+V لنسخ ولصق سير العمل.)
شارك المخرجات التي أرجعتها العُقدة الأخيرة
معلومات إعداد n8n الخاص بك
- إصدار n8n:
- قاعدة البيانات (الافتراضية: SQLite):
- إعداد n8n EXECUTIONS_PROCESS (الافتراضي: own, main):
- تشغيل n8n عبر (Docker, npm, n8n cloud, تطبيق سطح المكتب):
- نظام التشغيل:
مرحبا @Decoure_Ryan الطريقة الشائعة هي استخدام تحديد معدل لكل مستأجر بدلاً من حد واحد عام.
Webhook → Queue → Rate Limit Check → Worker → API
تتبع الطلبات لكل مستأجر باستخدام Redis أو قاعدة بيانات، ومعالجة الوظائف فقط عندما يكون هذا المستأجر ضمن حده المسموح به.
هذا يساعد على منع تأثر مستأجر واحد بالآخرين
التعامل مع حدود API مختلفة لكل مستأجر
تقليل الارتفاعات الناجمة عن إعادة المحاولات
للمستأجرين ذوي الحجم الكبير: قوائم انتظار منفصلة
عمال مخصصون حتى لا يؤثر حركة المرور الخاصة بهم على الجميع.
مرحباً @Decoure_Ryan في مجتمعنا! أنا Jay وأنا منشئ تم التحقق منه في n8n.
الطريقة الأكثر عملية الخاصة بـ n8n هنا هي استخدام عقدة Code قبل كل استدعاء API خارجي للتحقق من عداد وتحديثه لكل مستأجر. إذا كنت تستخدم Redis، فقم بتخزين المفتاح باسم rate_limit:{tenantId} مع فترة انتهاء الصلاحية تطابق فترة إعادة تعيين API - قم بالزيادة عند كل طلب، وإذا تجاوز المستأجر الحد، وجه إلى عقدة Wait بدلاً من المتابعة.
للمشكلة المتعلقة بانفجار إعادة المحاولة: بدلاً من إعادة محاولة n8n المدمجة (التي تُطلق فوراً ويمكن أن تؤدي إلى تضخيم 429s)، التقط مخرجات الخطأ من عقدة HTTP Request وجهها إلى عقدة Wait مع تأخير ثابت أو ديناميكي مسحوب من رأس الاستجابة Retry-After، ثم كرر العودة إلى الطلب.
إذا كنت في وضع queue، فإن حدود التزامن لكل سير عمل توفر عزل طبيعي لكل مستأجر عندما تعمل وظائف كل مستأجر في سير عمل فرعي مخصص - عيّن concurrency: 1 لكل سير عمل مستأجر والطابور يتعامل مع التحكم في السرعة لك بدون أي منطق عداد Redis.
إليك بعض الأشياء التي نجحت على نطاق واسع:
قوائم الانتظار لكل مستأجر هي الخيار الصحيح. تبدو قوائم الانتظار المشتركة مع التحكم بمعدل التدفق بسيطة في البداية، لكنها تنتهي عادة بمشاكل الجار الصاخب. قائمة الانتظار المعزولة توفر لك عزلاً حقيقياً دون الحاجة إلى منطق أولويات معقد.
أما بخصوص تنفيذ حاوية الرموز (Token Bucket)، فـ Redis قوي جداً، فقط قم بتخزين مفتاح لكل مستأجر باستخدام إعادة ملء قائمة على TTL. تأكد من عدم إطلاق جميع محاولات الإعادة في نفس الوقت بعد إعادة تعيين نافذة حد المعدل، استخدم التراجع الأسي مع الاهتزاز العشوائي.
أما بخصوص تخصيص العمال، فاعتبر نموذجاً متدرجاً: مجموعة صغيرة من العمال المخصصين للمستأجرين الذين لديهم أكبر حجم معاملات، والعمال المشتركون للجميع الآخرين. هذا سيتجنب الإفراط في التوفير مع الحفاظ على حماية معدل إنتاجية المستأجرين الأفضل.
شيء يتم تجاهله غالباً هو مراقبة عمق قائمة الانتظار ووقت الانتظار لكل مستأجر، وليس فقط حالات تجاوز حد المعدل. هنا حيث ستجد عادة الاختناق الحقيقي قبل حدوث مشكلة في اتفاقية مستوى الخدمة.
ما نوع واجهات برمجة التطبيقات الخارجية التي تستخدمها؟ البعض منها لديه مخصصات انفجار يمكن أن تخفف من مشكلة الإعادة بشكل كبير.
استخدام bucket للرموز المميزة لكل مستأجر في Redis هو الحدس الصحيح، والاختيار التصميمي الأساسي هو فرض الحد قبل وصول الوظيفة إلى العامل، وليس بداخله. إذا سحب العامل وظيفة ثم انتظر على حد معدل، فقد ربطت سعة العامل بلا فائدة، وهذا بالضبط مشكلتك “مستأجر واحد مشغول يستهلك سعة الجميع”.
إذاً الشكل هو: webhook إلى الطابور، ثم بوابة تتحقق من bucket المستأجر في Redis وتحرر الوظيفة للعامل فقط عندما يكون لدى ذلك المستأجر ميزانية، وإلا فإنها تعيد الوظيفة إلى الطابور بتأخير. يحافظ هذا على عدم حجب المستأجر المخنوق من قائمة الانتظار للآخرين.
على موجات المحاولة الجديدة: تحتاج محاولاتك إلى تراجع لكل مستأجر أيضاً، وليس واحد عام، وإلا فإن المستأجر الذي يكون بالفعل في حده سيعيد محاولته في نفس الجدار ويضخم الموجة. تراجع أسي مع العشوائية، محسوبة مقابل نفس bucket.
للعدالة الحقيقية عند مئات المستأجرين، الطوابير المنفصلة لعدد قليل من المستأجرين ذوي الحجم الكبير وطابور مشترك للذيل الطويل هي عادة التقسيم العملي. عزل طابور كامل لكل مستأجر أنظف لكن أكثر عملية تشغيلية. واحد أخير: ضع فحص على عمق الطابور لكل مستأجر حتى عندما يبدأ أحدها بالتراكم بعد حد معين تكتشف ذلك مبكراً، بدلاً من اكتشافه عندما يشتكي ذلك المستأجر من أن وظائفه متأخرة بساعات.