استهلالي
مرحبا هبر! اسمي بوريس وفي هذا العمل سأشارككم تجربتي في تصميم وتنفيذ خدمة بريدية جماعية كجزء من نظام شامل لتنبيه الطلاب إلى المعلمين (يشار إليه فيما يلي أيضًا باسم Ada) ، والذي أقوم بتنفيذه أيضًا.
الجحيم
ثم يلزم نفي عدد الانقطاعات في العملية التعليمية للأسباب التالية:
- لا يرغب المعلمون في مشاركة تفاصيل الاتصال الشخصية ؛
- الطلاب كذلك حقًا - ليس لديهم الكثير من الخيارات ؛
- نظرًا لخصوصيات جامعتي ، يضطر العديد من المعلمين أو يفضلون استخدام الأجهزة المحمولة دون الوصول إلى الإنترنت ؛
- إذا أرسلت رسائل من خلال قادة المجموعات ، فإن تأثير "الهاتف التالف" يلعب دوره ، بالإضافة إلى العامل "أوه ، لقد نسيت :(".
إنه يعمل حول ذلك :
- المعلم من خلال إحدى قنوات الاتصال المتاحة له: SMS ، Telegram ، تطبيق SPA - يرسل Ada نص الرسالة وقائمة العناوين ؛
- تبث Ada الرسالة المستلمة لجميع * الطلاب المهتمين من خلال قنوات الاتصال المختلفة.
* يتم توفير الوصول إلى الخدمة على أساس طلب تطوعي.
يفترض أن
- لن يتجاوز العدد الإجمالي للمستخدمين عشرة آلاف ؛
- سيتم الاحتفاظ بنسبة الطالب - المعلم / عضو مكتب الشؤون الداخلية (مكتب العميد ، المركز الصحي ، مكتب التسجيل العسكري ، إلخ) عند مستوى 10: 1 ؛
- : « », « ))0» ..
- ;
- , , ;
- ;
- : - - , , .
يتكون هذا العمل من خمسة أجزاء: تمهيدي ، تحضيري ، مفاهيمي ، موضوعي ، نهائي.
يمكنك تخطي الجزء التحضيري بأمان إذا كنت معتادًا على تفسير Redis لنمط Pub / Sub ، بالإضافة إلى آليات الأحداث ، والبرمجة النصية LUA والتعامل مع المفاتيح القديمة ، بالإضافة إلى ذلك ، من المستحسن للغاية أن يكون لديك فكرة على الأقل عن بنية الخدمات المصغرة للبرنامج.
في جزء الموضوع ، تمت مراجعة الكود في Python ، لكنني أعتقد أن هناك معلومات كافية بحيث يمكنك كتابة شيء مثل هذا على أي شيء.
تحضيري
تقريبي جدًا ومجرّد جدًا ~ 5 دقائق
Redis — [BSD 3-clause] , «-» ().
, .
, -, .
( ).
, , , LUA 5.1.
, .
, -, .
( ).
, , , LUA 5.1.
التفاصيل والمباشرة ~ 15 دقيقة
- Pub/Sub — Redis. , fire&forget , ,
PUBLISH,SUBSCRIBE-; - Redis Keyspace Notifications. ;
- EXPIRE — Redis. «How Redis expires keys»;
- Redis 6.0 Default Configuration File. . 939:948 (The default effort of the expire cycle…);
- EVAL — Redis.
EVALEVALSHA, «Atomicity of scripts», «Global variables protection» «Available libraries»,cjson; - Redis Lua Scripts Debugger. , . — ;
- . , .
المفاهيمي
نهج ساذج
الحل الأكثر وضوحا يمكن ان يخطر لك: طرق التسليم متعددة (
send_vk، send_telegramالخ) ومعالج واحد من شأنها أن ندعو لهم مع الوسائط المطلوبة.
مشكلة التمدد
إذا أردنا إضافة طريقة تسليم جديدة ، فسيتعين علينا تعديل الكود الحالي ، وهذا قيد على النظام الأساسي للبرنامج.
مشكلة الاستقرار
تعطلت إحدى الطرق = تعطلت الخدمة بالكامل.
مشكلة تطبيقية
تختلف واجهات برمجة التطبيقات لقنوات الاتصال المختلفة اختلافًا كبيرًا عن بعضها البعض من حيث التفاعل. على سبيل المثال ، يدعم فكونتاكتي المراسلات الجماعية ، ولكن ليس أكثر من مئات المستخدمين لكل مكالمة. Telegram غير موجود ، لكنه يسمح بمزيد من المكالمات في الثانية.
تعمل واجهة برمجة تطبيقات VK فقط عبر HTTP ؛ يحتوي Telegram على بوابة HTTP ، ولكنه أقل استقرارًا من MTProto وأقل توثيقًا جيدًا.
هناك الكثير من هذه الاختلافات: الحد الأقصى لطول الرسالة
random_idوالتفسير ومعالجة الأخطاء وما إلى ذلك. إلخ
كيفية التعامل مع هذا؟
تقرر الفصل بين عملية إدراج الرسائل وعمليات الإرسال (المشار إليها فيما يلي باسم السعاة) على المستوى التنظيمي ، بحيث لا يشك الأول في وجود هذا الأخير ، والعكس صحيح ، وستعمل Redis كحلقة وصل بينهما.
غير واضح؟ اطلب وجبة!
في غضون ذلك ، أنت تنتظر - دعني أقدم لك تفسيري لهذا العمل النبيل ، بدءًا من التصميم وانتهاءًا بالباب مغلقًا خلف الساعي.
- تضغط على زر "طلب" الأصفر الكبير ؛
- يجد Yandex.Food ساعيًا ، ويبلغ المطعم بالعناصر المحددة ويعيد رقم الطلب إليك من أجل التخفيف من عدم اليقين بشأن التوقعات ؛
- عند الانتهاء من الطهي ، يقوم المطعم بتحديث حالة الطلب ويقدم الطعام إلى الساعي ؛
- يقوم الساعي بدوره بتزويدك بالطعام ، ثم يقوم بتمييز الطلب على أنه مكتمل.
بالعافية!
العودة إلى التصميم
من المحتمل أن النموذج الوارد في الفقرة السابقة لا يتوافق تمامًا مع الواقع ، لكنها هي التي شكلت أساس الحل المطور.
ستسمى البيانات المرتبطة برقم الطلب بالمحفوظات ، فهي تتيح لك الإجابة على الأسئلة التالية في أي وقت :
- الذي أرسل؛
- ما أرسله ؛
- من أين؛
- إلى من؛
- من حصل عليه وكيف.
يتم إنشاء السجل جنبًا إلى جنب مع الطلب كمفتاحين منفصلين من مفاتيح Redis ، مرتبطين عبر لاحقة:
suffix={ }:{UNIX- }
=history:{suffix}
=delivery:{suffix}
يحدد الطلب متى سيرى الناقلون السجل مرة واحدة ، من أجل تغيير الإجابة على السؤال "من الذي استلمه وكيف" بعد اكتمال الإرسال.
تعمل "رؤية" السعاة من خلال الاشتراك في
DELمفاتيح الأحداث في النموذج delivery:*.
عندما تحين لحظة التسليم ، يحذف Redis مفتاح الطلب ، وبعد ذلك يبدأ الناقلون في معالجته.
نظرًا لوجود العديد من السعاة ، فهناك احتمال كبير للمنافسة في مرحلة تغيير التاريخ.
يمكنك تجنبه من خلال تحديد العملية المقابلة بشكل ذري - في Redis ، يتم ذلك من خلال برمجة LUA النصية.
ستتم مناقشة تفاصيل التنفيذ بالتفصيل في الفصل التالي. من المهم الآن الحصول على فكرة واضحة عن الحل ككل ، والتي يمكن أن يساعدها الشكل أدناه.
حالة التتبع
يمكن للعميل تتبع حالة التسليم من خلال مفتاح السجل ، الذي يتم إنشاؤه بواسطة طريقة API منفصلة للخدمة التي يتم تطويرها قبل وضع الرسالة في قائمة الانتظار (تمامًا مثل رقم الطلب الذي تم إنشاؤه بواسطة Yandex.Eda في البداية).
بعد إنشاء المفتاح ، يتم تعليق جهاز تعقب به مهلة (اختياريًا وأيضًا بطريقة منفصلة) ، والذي سيراقب عدد تغييرات السجل بواسطة شركات النقل (
SETالأحداث). الآن فقط يتم وضع الرسالة في قائمة الانتظار.
إذا لم يعثر الساعي على جهات اتصال المستلم في مجاله - قناة الاتصال ، فإنه يطلق حدثًا مصطنعًا من
SETخلال الأمر PUBLISH، وبذلك يظهر أنه "بخير" ولا داعي للانتظار أكثر من ذلك.
لماذا العبث بالأحداث في Redis عندما يكون لديك RabbitMQ و Celery
هناك خمسة أسباب موضوعية على الأقل لهذا:
يتم تنفيذ نظام الإخطار (الشامل) في شكل مجموعة من الخدمات المصغرة. من أجل الراحة ، تم نقل الواجهات ، وطرق تهيئة طبقات البيانات ، ونص الخطأ ، بالإضافة إلى بعض كتل المنطق المتكرر إلى المكتبة
core، والتي تعتمد بدورها على: gino(غلاف غير متزامن SQLAlchemy) ، aioredisو aiohttp.
يمكنك رؤية الكيانات المختلفة في الكود ، على سبيل المثال
User، Contactأو Allegiance. يتم عرض الروابط بينهما في الرسم البياني أدناه ، وهناك وصف موجز تحت المفسد.
حول الكيانات ~ 3 دقائق
— .
: , , . ., .
, : , Telegram, . .
[allegiance].
[supergroup].
[ownership] .
: , , . ., .
, : , Telegram, . .
[allegiance].
[supergroup].
[ownership] .
توليد مفتاح التاريخ
تسليم / معالجات / history_key / get - جيثب
طابور
تسليم / معالجات / طابور / وضع - جيثب
ملاحظة:
- التعليق 171: 174 ؛
- أن كل التلاعبات مع Redis [164: 179] ملفوفة في صفقة.
رؤية السعاة [94: 117]
الأساسية / التسليم - جيثب
تحديث التاريخ من قبل سعاة
core / redis_lua - GitHub
التعليمات [48:60] لا تحول القوائم الفارغة إلى قواميس (
[] -> {}) ، لأن معظم لغات البرمجة ، بما في ذلك CPython ، تفسرها بشكل مختلف عن LUA.
ISS: السماح بتمييز المصفوفات والكائنات من أجل إجراء تسلسل مناسب للكائن الفارغ - GitHub
تعقب
التسليم / المعالجات / المسار / البريد - جيثب - التنفيذ.
connect / telegram / handlers / select - GitHub [101: 134] - مثال للاستخدام في واجهة المستخدم.
سعاة
task_streamيتم التعامل مع
أي تسليم من (Sight Couriers) في coroutine غير متزامن منفصل.
الاستراتيجية العامة للتعامل مع قيود توقيت واجهات برمجة التطبيقات هي كما يلي: نحن لا نحسب RPS (الطلبات في الثانية) ، لكننا / نتفاعل / نستجيب بشكل صحيح للردود حسب النوع
http.TooManyRequests.
إذا نفذت الواجهة ، بالإضافة إلى العالمية (للتطبيق) ، حدود زمنية مخصصة أيضًا ، فستتم معالجتها بترتيب قائمة الانتظار ، أي أولاً نرسل إلى كل شخص يمكننا الانتظار وعندها فقط نبدأ في الانتظار ، إن لم يكن طويلاً.
برقية
courier / telegram - GitHub
كما ذكرنا سابقًا ، تتفوق واجهة MTProto من Telegram على نظيرتها HTTP من حيث الاستقرار وحجم الوثائق. للتفاعل معها ، سنستخدم حلاً جاهزًا ، وهو LonamiWebs / Telethon .
في تواصل مع
courier / vk -
تدعم GitHub VKontakte API المراسلات الجماعية عن طريق تمرير قائمة المعرّفات إلى طريقة messages.send (لا تزيد عن مائة) ، كما تتيح لك "لصق" ما يصل إلى خمسة وعشرين
messages.sendفي تنفيذ واحد ، مما يمنحنا 2500 رسالة لكل مكالمة.
حقيقة غريبة
API,
execute , .
الاخير
في هذا العمل ، تم اقتراح طريقة لتنظيم نظام تحذير جماعي متعدد القنوات. يفي الحل الناتج بالطلب (@ متطلبات المفتاح للخدمة البريدية) لمعظم الأطراف المهتمة ، ويفترض أيضًا إمكانية التوسع.
العيب الرئيسي هو تأثير Fire & forget Pub / Sub ، أي إذا كان حذف مفتاح الطلب ضروريًا في وقت مرض أحد الناقلين ، فلن يتلقى أي شخص أي شيء في المجال المقابل ، وهو ما سينعكس في السجل.