استيعاب المحتوى الموسيقي على نطاق واسع باستخدام DDEX: الجزء الثاني

تحليل رسائل إعلام الموارد الإلكترونية لـ DDEX (ERN)

خلاصة

في المشاركة الأخيرة ، قمت بتقديم Data Definition EXchange (DDEX) كتنسيق يستخدم في صناعة الموسيقى لتوزيع المنتجات رقميًا. لقد وصفت أيضًا الأهداف والغايات التي كانت لدينا في استهلاك موارد DDEX لتوفير محتوى صوتي لمستخدمينا.

في هذا المنشور ، سوف أتناول التفاصيل الفنية لكيفية تنفيذها.

سير العمل

تم استلام رسالة جديدة

يبدأ سير العمل لدينا عندما نتلقى رسالة إعلام الموارد الإلكترونية (ERN) من موفر الموسيقى. يتم ذلك عبر AWS S3. كل مزود لديه دلو مخصص ، يمكن الوصول إليها فقط من قبلهم.

نستخدم المشغل لإطلاق وظيفة Lambda المناسبة عند تلقي الرسالة.

يظهر هنا تكوين المشغل ، مما يدل على أن الملفات الموضوعة في المجلد الوارد بملحق .xml ستؤدي إلى تشغيل وظيفة Lambda.

الوصول إلى الرسالة

عندما يتم تشغيل وظيفة Lambda ، يتم تمرير المعلمة إلى الوظيفة. تحتوي هذه المعلمة على اسم دلو S3 واسم المفتاح (اسم ملف a.k.a) للملف الذي أدى إلى تشغيل الوظيفة. نستخدم هذه المعلومات لتنزيل الملف إلى وظيفة Lambda ، حيث يمكن قراءته.

عملية def (الحدث ، السياق):
    "" "
    Lambda معالج لمعالجة رسائل ERN جديدة
    "" "
    # الحصول على ملف s3
    للتسجيل في الحدث ['السجلات']:
        دلو = سجل ['s3'] ['دلو'] ['اسم']
        مفتاح = سجل ['s3'] ['كائن'] ['مفتاح']
        file_path = '/tmp/{0}'.format(key.split("/") Budap-1])
        s3_client.download_file (دلو ، مفتاح ، file_path)
        ''

ستلاحظ أننا نزّل الملف إلى الدليل / tmp /. هذا هو الموقع الوحيد القابل للكتابة داخل وظيفة Lambda ، وبقية نظام الملفات للقراءة فقط. يقتصر أيضًا على 500 ميجابايت (سترى سبب أهمية معرفة ذلك لاحقًا).

تحليل الرسالة

الآن وقد أصبح لدينا رسالة ، فقد حان الوقت لتحليلها ومعرفة ما بداخلها.

هناك ثلاث رسائل أساسية نتوقعها ونعمل عليها:

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

لتحليل الرسالة ، استخدمنا ElementTree. إنه سهل الاستخدام وغني بالميزات وجزء من بيثون الأساسية. كانت احتياجاتنا بسيطة للغاية ، لذلك لم أر أي سبب للنظر إلى أبعد من ذلك.

إليك المنكر: أي رسالة معيّنة من المرسل تحتوي على الكثير من المعلومات. تحتوي الرسالة النموذجية على ما يلي:

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

كشركة لياقة صوتية ، نستخدم الموسيقى لإنشاء قوائم تشغيل يتم استخدامها من قبل المدربين لدينا كموسيقى تصويرية لممارسة التمرين الموجه. هذا يعني أننا ربما لن نشغل الألبوم أبدًا. لن نقوم أيضًا ببيع قرص مضغوط فعلي (أو شريط كاسيت أو ألبوم أو 8 مقاطع أو تنزيل). بمعنى آخر ، بعض الصفقات لا تنطبق علينا.

مع كل هذه المعرفة الجديدة ، شعرت أن لدي معلومات كافية لبدء بناء سير العمل لدينا.

التنفيذ

الحفاظ على سلامة المحتوى المبتلع

الخطوة الأولى هي تحويل رسالة XML إلى تنسيق قابل للاستخدام بالكامل. هذا يضمن أن تكون لدينا دائمًا الرسالة الكاملة التي قد تكون ضرورية لاستكشاف الأخطاء وإصلاحها أو التحقق من أننا اتخذنا الإجراء الصحيح استنادًا إلى محتويات الرسالة.

لقد استخدمت SQLAlchemy لإنشاء مخطط قاعدة بيانات. هناك الكثير من الكارهين ORM ، وفي بعض الحالات لها ما يبررها. في هذه الحالة ، الإجراء الوحيد المطلوب هو حفظ الرسالة في قاعدة بيانات. في تجربتي ، يمكن تتبع المشكلة التي يواجهها معظم الناس مع ORMs لأداء استعلامات SQL معقدة (والتي يمكن تتبعها عدة مرات لتصميم مخطط الفقراء). شعرت بالثقة من أننا يمكن أن نتجنب ذلك هنا ، واستخدام ORM يعطيني كائن فئة Python لطيف يمثل رسالة ERN بالكامل.

بعد فوات الأوان ، كنت أتمنى لو كنت قد استخدمت MongoDB لهذا بدلاً من Postgres. من وجهة نظر علائقية ، يبدو هذا واضحًا جدًا ، وهو كذلك. بعد عمليات الدمج المتعددة ، علمت أن كل مرسل يضيف حقولًا فريدة له أو يستخدم تنسيق بيانات مختلفًا.

خمين ما؟

إذا لم أكن رمزًا لذلك ، فلن يتم التقاطه.

إذا كنت قد استخدمت MongoDB على الرغم من ذلك ، كان بإمكاني تحويل XML إلى JSON وحفظه إلى ديسيبل. بالإضافة إلى ذلك ، لدى Mongo إمكانات استعلام ممتازة وفي معظم الحالات يقوم بعمل استثنائي في الاستعلام عبر مجموعات البيانات المتفرقة.

الدرس المستفاد.

تحقق من البيانات المطلوبة

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

إذا لم يكن message_header.find ('MessageId') بلا:
            ern.message_id = message_header.find ('MessageId'). النص
        آخر:
            رفع استثناء ("رسالة مفقود")

باستخدام طريقة .find () يتحقق لمعرفة ما إذا كان العنصر موجودًا في XML ، وإذا تم العثور على استرداد القيمة باستخدام .text.

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

قم بتنزيل وتخزين الموارد

والخطوة التالية هي التكرار من خلال ResourceList. يوفر هذا جميع الموارد (المسارات الصوتية ، أعمال الألبوم الفنية ، إلخ) الموجودة في هذه الرسالة. يجب تنزيل كل واحدة منها من المرسل.

  T5 
  FLAC 
  2 
  44.1 
  16 
  كاذبة 
 <ملف>
     https://s3.amazonaws.com/XXXXXX/XXXX.flac 
    
         014a089377bb23c80c693d10065f03a9 
         MD5 
    
  

لأننا نستخدم AWS Lambda ، قدمت مشكلتان فريدتان. أولاً ، لدى كل وظيفة من وظائف Lambda وقت تشغيل أقصى 5 دقائق. ثانياً ، تعمل وظيفة Lambda في نظام ملفات للقراءة فقط ، باستثناء دليل 500mb / tmp.

تتبع = طلبات .get (url ، دفق = صواب)
    إذا track.ok:
        file_name = '/ tmp /' + url.split ('/') [- 1] .split ('؟') [0]
        مع open (file_name ، 'wb') كـ f:
            بالنسبة للمقطع في track.iter_content (chunk_size = 1024):
                إذا قطعة:
                    f.write (قطعة)
        إذا get_hash (file_name) == hashsum:
            إرجاع file_name
        آخر:
            رفع استثناء ("لم يتطابق التجزئة مع الملف الذي تم تنزيله")
    آخر:
        if track.status_code> = 400 و track.status_code <500:
            رفع الاستثناءات. ملف 400Exception ("خطأ في الوصول إلى الملف")
        elif track.status_code> = 500:
            رفع الاستثناءات. File500Exception ("واجه HTTP 5XX ملف التنزيل")

باستخدام مكتبة طلبات python ، نتحقق أولاً من أن عنوان URL للملف يُرجع استجابة HTTP 200 if track.ok. بعد ذلك ، نقوم بتنزيل الملف وحفظه في المجلد / tmp ، ثم نتحقق من تطابق تجزئة MD5 مع القيمة المتوفرة في رسالة XML. هذا يضمن الملف الذي تم تنزيله بالكامل ، دون تلف.

بمجرد تنزيلها والتحقق منها ، نستخدم مكتبة boto3 لتحميل الملف إلى دلو S3 الخاص بنا.

res = upload.Bucket (UPLOAD_BUCKET) .put_object (Key = file_name.split ("/") [- 1] ، نص = بيانات ، ServerSideEncryption = 'aws: kms')

لاحظ أننا نحدد ServerSideEncryption كذلك بحيث يتم تشفير الملفات الموجودة في دلو S3 الخاص بنا.

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

في المسار 1. ‍♂

للتغلب على ذلك ، أضفت فحصًا سريعًا لمعرفة ما إذا كان الملف الذي تم تنزيله موجودًا بالفعل في دلو S3 النهائي الخاص بنا:

العميل = boto3.client ('s3')
    محاولة:
        استجابة = client.head_object (دلو = دلو ، مفتاح = مفتاح)
        عودة صحيح
    باستثناء ClientError كـ e:
        if int (e.response ['Error'] ['Code']) == 404:
            عودة كاذبة

هذا يعني أنه يمكن تخطي الملفات التي تم تنزيلها مسبقًا ، مما يسمح للوظيفة بالتركيز على الملفات المتبقية في رسالة ERN.

بعد ذلك ، وصلت إلى العدد الثاني: الحد 500 ميجابايت. نظرًا لأن كل رسالة من رسائل ERN تؤدي وظيفة Lambda الخاصة بها ، فلا تعتقد أن البيانات التي لا معنى لها ستكون مشكلة (على الأقل لم أفعل). اتضح هو عليه. عندما تنتهي وظيفة Lambda من التنفيذ ، إذا تم إطلاق مشغل آخر داخل نافذة معينة ، يتم إعادة استخدام وظيفة Lambda هذه.

هذا يعني أن مساحة / tmp قد تحتوي بالفعل على ملفات فيه. بمجرد وصولك إلى 500 ميجابايت ، ستفشل الوظائف بسبب نقص مساحة القرص. نتيجة لذلك ، أضفت

os.remove (اسم_الملف)

مباشرة بعد تحميل الملف إلى دلو S3 الخاص بنا لضمان عدم وجود ملفات باقية.

احفظ الرسالة التي تم تحليلها في قاعدة البيانات الرئيسية

في هذه المرحلة ، نحن

  • التحقق من صحة الحقول المطلوبة لدينا
  • تنزيل الموارد المحددة في الرسالة
  • حفظ الملفات إلى دلو S3 المشفر

عندها فقط أكتب الرسالة إلى قاعدة البيانات. هذا يضمن أن الرسالة تتم معالجتها بالكامل وبنجاح عند كتابتها إلى قاعدة البيانات. لأنني استخدمت SQLAlchemy كـ ORM وقمت بتحليل رسالة ERN إلى كائن Python ، فإن الحفظ بسيط مثل

session.add (ERN)
session.commit ()

أخبر شخصًا ما انتهيت منه

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

استجابة =طلبات. مشاركة (WS_URL ، بيانات = طلب ، مصادقة = (WS_USER ، WS_PWD) ، رؤوس = رؤوس)

عنوان URL لخدمة الويب واسم المستخدم وكلمة المرور كلها متغيرات البيئة. هذا يعني أنه يمكنني تعيينهم لكل وظيفة ولن أضطر أبدًا إلى تخزين بيانات الاعتماد في الكود. الحمولة إلى خدمة الويب هي ملف XML مع عنصر SuccessfullyIngestedByDistributor .

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

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

التالي

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