بناء الأنظمة الكبيرة والمهام الخلفية طويلة المدى. الائتمان: إلياس شيبي على Unsplash قبل أشهر، توليت الدور الذي تطلب بناء البنية التحتيةبناء الأنظمة الكبيرة والمهام الخلفية طويلة المدى. الائتمان: إلياس شيبي على Unsplash قبل أشهر، توليت الدور الذي تطلب بناء البنية التحتية

بناء سبوتيفاي للخطب الدينية.

2025/12/11 21:15

البناء للأنظمة الكبيرة والمهام الخلفية طويلة المدى.

الائتمان: إلياس شيبي على Unsplash

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

الاهتمام الأساسي:

خلال عملية البناء، جاء كل تكرار كاستجابة للحاجة الفورية وغالبًا "الشاملة". كان الاهتمام الأولي هو وضع المهام في قائمة الانتظار، والتي كانت كافية مع Redis؛ ببساطة أطلقنا ونسينا. أعطانا Bull MQ في إطار عمل NEST JS تحكمًا أفضل في عمليات إعادة المحاولة والتراكمات وقائمة انتظار الرسائل الميتة. محليًا ومع بعض الحمولات في الإنتاج، حصلنا على تدفق الوسائط بشكل صحيح. سرعان ما أثقلنا وزن المراقبة:
السجلات → سجل المهام (الطلبات، الاستجابات، الأخطاء).
المقاييس → كم / كم مرة تعمل هذه المهام، تفشل، تكتمل، إلخ.
التتبعات → المسار الذي اتخذته المهمة عبر الخدمات (الوظائف/الطرق المستدعاة ضمن مسار التدفق).

يمكنك حل بعض هذه المشكلات من خلال تصميم واجهات برمجة التطبيقات وبناء لوحة تحكم مخصصة لتوصيلها، ولكن مشكلة قابلية التوسع ستكون كافية. وفي الواقع، قمنا بتصميم واجهات برمجة التطبيقات.

البناء للمراقبة

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

على سبيل المثال، سيؤدي حدث Transcription.request إلى تشغيل وظيفة TranscribeAudio. قد تحتوي هذه الوظيفة على خطوات تشغيل لـ: fetch_audio_metadata و deepgram_transcribe و parse_save_trasncription و notify_user.

تفكيك سير العمل: وظيفة Inngest وخطوات التشغيل

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

  • المنطق الذري: تنفذ الوظيفة منطق عملك خطوة بخطوة. إذا فشلت خطوة، يتم الحفاظ على حالة التشغيل بأكملها، ويمكن إعادة محاولة التشغيل. هذا يعيد تشغيل الوظيفة من البداية. لا يمكن إعادة محاولة الخطوات الفردية أو خطوات التشغيل بمعزل.
  • تسلسل الاستجابة: يتم تعريف خطوة التشغيل من خلال استجابتها. يتم تسلسل هذه الاستجابة تلقائيًا، وهو أمر ضروري للحفاظ على هياكل البيانات المعقدة أو ذات الأنواع القوية عبر حدود التنفيذ. يمكن لخطوات التشغيل اللاحقة تحليل هذه الاستجابة المتسلسلة بشكل موثوق، أو يمكن دمج المنطق في خطوة واحدة للكفاءة.
  • فك الارتباط والجدولة: داخل الوظيفة، يمكننا وضع أحداث جديدة تابعة في قائمة الانتظار أو جدولتها بشكل مشروط، مما يتيح أنماط التوسع/التجميع المعقدة والجدولة طويلة المدى تصل إلى سنة. يمكن التقاط الأخطاء والنجاحات في أي نقطة ومعالجتها في مراحل لاحقة من سير العمل.

ملخص وظيفة Inngest:

import { inngest } from 'inngest-client';

export const createMyFunction = (dependencies) => {
return inngest.createFunction(
{
id: 'my-function',
name: 'My Example Function',
retries: 3, // retry the entire run on failure
concurrency: { limit: 5 },
onFailure: async ({ event, error, step }) => {
// handle errors here
await step.run('handle-error', async () => {
console.error('Error processing event:', error);
});
},
},
{ event: 'my/event.triggered' },
async ({ event, step }) => {
const { payload } = event.data;

// Step 1: Define first step
const step1Result = await step.run('step-1', async () => {
// logic for step 1
return `Processed ${payload}`;
});

// Step 2: Define second step
const step2Result = await step.run('step-2', async () => {
// logic for step 2
return step1Result + ' -> step 2';
});

// Step N: Continue as needed
await step.run('final-step', async () => {
// finalization logic
console.log('Finished processing:', step2Result);
});

return { success: true };
},
);
};

يوفر نموذج Inngest المدفوع بالأحداث رؤية تفصيلية لكل تنفيذ لسير العمل:

  • تتبع الأحداث الشامل: يتم تسجيل كل تنفيذ وظيفة في قائمة الانتظار مقابل الحدث الأصلي. هذا يوفر مسارًا واضحًا ورفيع المستوى لجميع الأنشطة المتعلقة بإجراء مستخدم واحد.
  • رؤى تشغيل مفصلة: لكل تنفيذ وظيفة (النجاحات والإخفاقات)، يوفر Inngest سجلات مفصلة من خلال تقارير ack (الإقرار) و nack (الإقرار السلبي). تتضمن هذه السجلات تتبعات الأخطاء، وحمولات الطلب الكاملة، وحمولات الاستجابة المتسلسلة لكل خطوة تشغيل فردية.
  • مقاييس التشغيل: بعيدًا عن السجلات، اكتسبنا مقاييس حاسمة حول صحة الوظيفة، بما في ذلك معدلات النجاح، ومعدلات الفشل، وعدد إعادة المحاولات، مما يتيح لنا مراقبة موثوقية وزمن استجابة سير العمل الموزع بشكل مستمر.

البناء للمرونة

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

لمعالجة هذا وفرض متانة الحدث الصارمة، قمنا بتنفيذ نظام قائمة انتظار مخصص كمخزن مؤقت.

كان نظام قائمة انتظار AWS البسيط (SQS) هو النظام المختار (على الرغم من أن أي نظام قائمة انتظار قوي قابل للتنفيذ)، نظرًا لبنيتنا التحتية الحالية على AWS. قمنا بتصميم نظام قائمة انتظار مزدوج: قائمة انتظار رئيسية و قائمة انتظار الرسائل الميتة (DLQ).

أنشأنا بيئة عمل Elastic Beanstalk (EB) مهيأة خصيصًا لاستهلاك الرسائل مباشرة من قائمة الانتظار الرئيسية. إذا فشلت معالجة رسالة في قائمة الانتظار الرئيسية بواسطة عامل EB عددًا محددًا من المرات، تنقل قائمة الانتظار الرئيسية تلقائيًا الرسالة الفاشلة إلى DLQ المخصصة. هذا يضمن عدم فقدان أي حدث بشكل دائم إذا فشل في التشغيل أو تم التقاطه بواسطة Inngest. تختلف بيئة العمل هذه عن بيئة خادم الويب EB القياسية، حيث تتمثل مسؤوليتها الوحيدة في استهلاك الرسائل ومعالجتها (في هذه الحالة، إعادة توجيه الرسالة المستهلكة إلى نقطة نهاية Inngest API).

فهم الحدود والمواصفات

جزء غير معلن وذو صلة من بناء البنية التحتية على نطاق المؤسسة هو أنها تستهلك الموارد، وهي طويلة المدى. توفر بنية الخدمات المصغرة قابلية التوسع لكل خدمة. ستدخل التخزين وذاكرة الوصول العشوائي ومهلات الموارد في اللعبة. على سبيل المثال، انتقلت مواصفاتنا لنوع مثيل AWS بسرعة من t3.micro إلى t3.small، وهي الآن مثبتة على t3.medium. بالنسبة للمهام الخلفية طويلة المدى وكثيفة وحدة المعالجة المركزية، يفشل التوسع الأفقي باستخدام مثيلات صغيرة لأن الاختناق هو الوقت الذي يستغرقه معالجة مهمة واحدة، وليس حجم المهام الجديدة التي تدخل قائمة الانتظار.

المهام أو الوظائف مثل الترميز والتضمين عادة ما تكون مقيدة بوحدة المعالجة المركزية و مقيدة بالذاكرة. مقيدة بوحدة المعالجة المركزية لأنها تتطلب استخدامًا مستدامًا ومكثفًا لوحدة المعالجة المركزية، و مقيدة بالذاكرة لأنها غالبًا ما تتطلب ذاكرة وصول عشوائي كبيرة لتحميل نماذج كبيرة أو معالجة ملفات أو حمولات كبيرة بكفاءة.

في النهاية، وفرت هذه البنية المعززة، التي تضع متانة SQS والتنفيذ المتحكم فيه لبيئة عمل EB مباشرة في اتجاه تدفق واجهة برمجة تطبيقات Inngest، مرونة أساسية. حققنا ملكية صارمة للأحداث، وألغينا ظروف التسابق أثناء ارتفاعات حركة المرور، واكتسبنا آلية رسائل ميتة غير متطايرة. استفدنا من Inngest لقدراته على تنسيق سير العمل وتصحيح الأخطاء، بينما اعتمدنا على بدائيات AWS لتحقيق أقصى قدر من إنتاجية الرسائل ومتانتها. النظام الناتج ليس قابلاً للتوسع فحسب، بل قابل للتدقيق بدرجة عالية، مما يترجم بنجاح المهام الخلفية المعقدة وطويلة المدى إلى خطوات صغيرة آمنة وقابلة للمراقبة ومتسامحة مع الفشل.


تم نشر بناء Spotify للمواعظ في الأصل في Coinmonks على Medium، حيث يواصل الناس المحادثة من خلال تسليط الضوء والرد على هذه القصة.

إخلاء مسؤولية: المقالات المُعاد نشرها على هذا الموقع مستقاة من منصات عامة، وهي مُقدمة لأغراض إعلامية فقط. لا تُظهِر بالضرورة آراء MEXC. جميع الحقوق محفوظة لمؤلفيها الأصليين. إذا كنت تعتقد أن أي محتوى ينتهك حقوق جهات خارجية، يُرجى التواصل عبر البريد الإلكتروني service@support.mexc.com لإزالته. لا تقدم MEXC أي ضمانات بشأن دقة المحتوى أو اكتماله أو حداثته، وليست مسؤولة عن أي إجراءات تُتخذ بناءً على المعلومات المُقدمة. لا يُمثل المحتوى نصيحة مالية أو قانونية أو مهنية أخرى، ولا يُعتبر توصية أو تأييدًا من MEXC.