نشر أندرو غودوين DEP 0009: جانغو القادر على Async في 9 مايو ، وتمت الموافقة عليه من قبل مجلس جانغو التقني في 21 يوليو ، لذلك يمكنك أن تأمل أنه بحلول وقت إصدار جانغو 3.0 ، سيكونون قادرين على القيام بشيء مثير للاهتمام. لقد تم ذكره بالفعل في مكان ما في تعليقات هبر ، لكنني قررت نقل هذه الأخبار إلى جمهور أوسع من خلال ترجمتها - بشكل أساسي لأولئك الذين ، مثلي ، لا يتابعون أخبار جانغو بشكل خاص.
تم تطوير Python غير المتزامن لسنوات عديدة ، وفي نظام جانغو البيئي ، جربناه في Channels مع التركيز الأساسي على دعم مقبس الويب.
مع تطور النظام الإيكولوجي ، أصبح من الواضح أنه بينما لم تكن هناك حاجة ملحة لتوسيع Django لدعم بروتوكولات غير HTTP مثل مآخذ الويب ، فإن الدعم غير المتزامن من شأنه أن يوفر العديد من الفوائد لإطار عمل القوالب التقليدية لنموذج عرض Django.
يتم وصف الفوائد في قسم الدافع أدناه ، ولكن الاستنتاج العام الذي توصلت إليه هو أننا نحصل على الكثير من Django غير المتزامن الذي يستحق العمل الشاق الذي يتطلبه. أعتقد أيضًا أنه من المهم جدًا إجراء تغييرات بطريقة تكرارية يدعمها المجتمع والتي لن تعتمد على واحد أو اثنين من المساهمين القدامى الذين قد يحترقون.
على الرغم من أن الوثيقة تم تعيينها على أنها "ميزة" DEP ، كل هذا يعني أنها جزءًا من عملية DEP. إن نطاق التغييرات المقترحة أدناه كبير بشكل لا يصدق ، ومن المحتمل أن يفشل إطلاقها كعملية تقليدية أحادية الميزة.
بالطبع ، من خلال هذا المستند ، من المهم أن نتذكر فلسفة Django ، والتي هي الحفاظ على كل شيء آمن ومتخلف. لا تهدف الخطة إلى إزالة متزامن Django - الخطة هي الاحتفاظ به في شكله الحالي ، ولكن إضافة عدم التزامن كخيار لأولئك الذين يعتقدون أنهم بحاجة إلى أداء إضافي أو مرونة.
هل هذه وظيفة عملاقة؟ بالطبع لكنني أشعر أن هذا يمكن أن يغير بشكل كبير مستقبل جانغو - لدينا الفرصة لاتخاذ إطار مجرب ومجتمع لا يصدق ، وتقديم مجموعة جديدة تمامًا من الخيارات التي كانت مستحيلة في السابق.
لقد تغير الويب ، ويجب أن يتغير Django معه ، ولكن وفقًا لمُثُلنا ، تكون ميسورة التكلفة وآمنة بشكل افتراضي ومرن مع نمو المشروعات وتغيير احتياجاتها. في عالم تخزين البيانات السحابية ، والعمارة الموجهة نحو الخدمات ، والواجهة الخلفية كأساس لمنطق الأعمال المعقدة ، تعد القدرة على القيام بالأشياء بشكل تنافسي أساسية.
يحدد هذا DEP خطة أعتقد أنها ستقودنا هناك. هذه رؤية أؤمن بها حقًا وسأعمل معها للمساعدة في عمل كل شيء ممكن. في الوقت نفسه ، هناك ما يبرر التحليل الدقيق والشك. أطلب النقد البناء الخاص بك ، وكذلك ثقتكم. تعتمد Django على مجتمع من الناس والتطبيقات التي يقومون بإنشائها ، وإذا كنا بحاجة إلى تحديد الطريق إلى المستقبل ، فيجب علينا أن نفعل ذلك معًا.
وصف قصير
سنضيف دعمًا للتمثيلات غير المتزامنة والبرامج الوسيطة و ORM والعناصر المهمة الأخرى إلى Django.
سيتم ذلك عن طريق تشغيل التعليمات البرمجية المتزامنة في مؤشرات الترابط ، واستبدالها تدريجياً بالكود غير المتزامن. ستستمر واجهات برمجة التطبيقات المتزامنة في الوجود وستكون مدعومة بالكامل ، وبمرور الوقت ستتحول إلى مغلف متزامن للرمز غير المتزامن في البداية.
سيقوم وضع ASGI بإطلاق تطبيق Django كتطبيق غير متزامن أصلي. سيؤدي وضع WSGI إلى تشغيل حلقة حدث منفصلة في كل مرة يتم فيها الوصول إلى Django ، بحيث تكون الطبقة غير المتزامنة متوافقة مع الخادم المتزامن.
تعد عملية تعدد مؤشرات الترابط حول ORM معقدة وتتطلب مفهومًا جديدًا لسياقات الاتصال والخيوط اللزجة لتشغيل رمز ORM المتزامن.
ستواصل العديد من أجزاء Django العمل بشكل متزامن ، وستكون أولويتنا هي دعم المستخدمين في كتابة طرق العرض في كلا الأسلوبين ، مما يسمح لهم باختيار أفضل أسلوب للعرض التقديمي الذي يعملون عليه.
ستحتاج بعض الوظائف ، مثل القوالب وذاكرة التخزين المؤقت ، إلى DEPs منفصلة ودراسات حول كيفية جعلها غير متزامنة تمامًا. يركز DEP بشكل أساسي على تدفق HTTP وعرض الوسيطة و ORM.
سيكون هناك توافق كامل للخلف. يجب تشغيل مشروع Django 2.2 القياسي في Django غير المتزامن (سواء كان 3.0 أو 3.1) دون تغيير.
يركز هذا الاقتراح على تنفيذ الأجزاء الصغيرة التكرارية مع وضعها التدريجي في الفرع الرئيسي لتجنب مشاكل الشوكة الطويلة العمر والسماح لنا بتغيير المسار أثناء اكتشافنا للمشاكل.
هذه فرصة جيدة لجذب أعضاء جدد. يجب علينا تمويل المشروع حتى يحدث هذا بشكل أسرع. يجب أن يكون التمويل على نطاق لم نعتد عليه.
مواصفة
الهدف العام هو جعل كل جزء واحد من Django ، والذي يمكن حظره - وليس فقط الحسابات المرتبطة بوحدة المعالجة المركزية - يصبح غير متزامن (يتم تشغيله في حلقة حدث غير متزامن بدون أقفال).
وهذا يشمل الميزات التالية:
- الطبقات الوسيطة (الوسيطة)
- العرض (المشاهدات)
- ORM
- قوالب
- تجريب
- التخزين المؤقت
- التحقق من صحة النموذج
- البريد الإلكتروني
ومع ذلك ، لا يشمل هذا أشياء مثل التدويل ، والتي لن تجلب أي مكاسب في الأداء ، لأن هذه مهمة مرتبطة بوحدة المعالجة المركزية والتي يتم تنفيذها أيضًا بسرعة ، أو عمليات ترحيل مفردة الترابط عند إطلاقها من خلال أمر الإدارة.
كل وظيفة فردية تصبح غير متزامنة في الداخل ستوفر أيضًا واجهة متزامنة متوافقة مع الإصدارات السابقة من API الحالي (في 2.2) للمستقبل المنظور - يمكننا تغييرها بمرور الوقت لتحسينها ، لكن واجهات برمجة التطبيقات المتزامنة لن تذهب إلى أي مكان.
فيما يلي نظرة عامة على كيفية تحقيق ذلك تقنيًا ، ثم يتم تقديم تفاصيل تنفيذ محددة لمناطق محددة. إنها ليست شاملة لجميع وظائف Django ، ولكن إذا حققنا هذا الهدف الأولي ، فسنضم جميع حالات الاستخدام تقريبًا.
يناقش الجزء الأخير من هذا القسم ، "الإجراء" ، أيضًا كيف يمكن تنفيذ هذه التغييرات بشكل تدريجي وعن طريق العديد من فرق التطوير بالتوازي ، وهو أمر مهم لإكمال هذه التغييرات بمساعدة المتطوعين في فترة زمنية معقولة.
مراجعة فنية
المبدأ الذي يسمح لنا بالحفاظ على التطبيقات المتزامنة وغير المتزامنة في نفس الوقت هو القدرة على تشغيل نمط واحد داخل الآخر.
ستذهب كل وظيفة إلى ثلاث مراحل للتنفيذ:
- متزامن فقط (نحن هنا)
- تنفيذ متزامن مع المجمع غير المتزامن
- تنفيذ غير متزامن مع المجمع متزامن
المجمع غير متزامن
أولاً ، سيتم لف الكود المتزامن الموجود في واجهة غير متزامنة ، والذي يقوم بتشغيل الكود المتزامن في تجمع مؤشرات الترابط. سيتيح لنا ذلك تصميم واجهة غير متزامنة وتوفيرها بسرعة نسبية ، دون الحاجة إلى إعادة كتابة جميع التعليمات البرمجية المتاحة للتزامن.
تتوفر مجموعة الأدوات الخاصة بهذا بالفعل في asgiref كدالة sync_to_async
، والتي تدعم أشياء مثل معالجة الاستثناءات أو سلاسل الصفحات (المزيد في هذا أدناه).
من المحتمل ألا يؤدي تشغيل التعليمات البرمجية في سلاسل الرسائل إلى زيادة في الأداء - من المحتمل أن تؤدي النفقات العامة التي تظهر إلى إبطاء قليلاً عندما تقوم بتشغيل الشفرة الخطية العادية فقط - لكنها ستسمح للمطورين بالبدء في تشغيل شيء تنافسي والتعود على الميزات الجديدة.
بالإضافة إلى ذلك ، هناك عدة أجزاء من Django حساسة للبدء في نفس الخيط عند الوصول المتكرر ؛ على سبيل المثال ، معالجة المعاملات في قاعدة البيانات. إذا قمنا بلف بعض التعليمات البرمجية في atomic()
والتي من ثم الوصول إلى ORM من خلال مؤشرات ترابط عشوائية مأخوذة من التجمع ، فلن يكون للمعاملة أي تأثير ، حيث إنها كانت مرتبطة بالاتصال داخل السلسلة التي بدأت المعاملة.
في مثل هذه المواقف ، يكون "مؤشر الترابط اللاصق" مطلوبًا حيث يستدعي السياق غير المتزامن بالتتابع جميع التعليمات البرمجية المتزامنة في نفس مؤشر الترابط بدلاً من دفعها إلى تجمع مؤشرات الترابط مع الحفاظ على السلوك الصحيح لـ ORM والأجزاء الأخرى الحساسة لمؤشر الترابط. جميع أجزاء Django التي نشك في حاجتها إليها ، بما في ذلك ORM بالكامل ، ستستخدم إصدار sync_to_async
الذي يأخذ هذا في الاعتبار ، لذلك كل شيء آمن بشكل افتراضي. سيتمكن المستخدمون من تعطيل هذا بشكل انتقائي لتنفيذ استعلام تنافسي - لمزيد من التفاصيل ، انظر "ORM" أدناه.
التنفيذ غير المتزامن
والخطوة التالية هي إعادة كتابة تنفيذ الوظيفة إلى رمز غير متزامن ثم تقديم الواجهة المتزامنة من خلال غلاف ينفذ التعليمات البرمجية غير المتزامنة في حلقة حدث لمرة واحدة. هذا متاح بالفعل في asgiref async_to_sync
.
ليس من الضروري إعادة كتابة جميع الوظائف دفعة واحدة للانتقال بسرعة إلى المرحلة الثالثة. يمكننا تركيز جهودنا على الأجزاء التي يمكننا القيام بها بشكل جيد والتي تحظى بدعم مكتبات الأطراف الثالثة ، مع مساعدة بقية نظام Python البيئي في أشياء تتطلب مزيدًا من العمل لتنفيذ عدم التزامن الأصلي ؛ ويناقش هذا أدناه.
تعمل هذه النظرة العامة مع جميع وظائف Django تقريبًا والتي يجب أن تصبح غير متزامنة ، باستثناء تلك الأماكن التي لا توفر Python مكافئات دالة غير متزامنة لها والتي نستخدمها بالفعل. ستكون النتيجة إما تغيير في الطريقة التي يعرض بها Django واجهة برمجة التطبيقات الخاصة به في وضع غير متزامن ، أو العمل مع مطوري Python الأساسيين للمساعدة في تطوير ميزات Python غير المتزامنة.
Threadlocals
واحدة من التفاصيل الأساسية لتنفيذ Django التي يجب ذكرها بشكل منفصل عن معظم الوظائف الموضحة أدناه هي threadlocals. كما يوحي الاسم ، تعمل threadlocals داخل مؤشر ترابط ، وعلى الرغم من أن Django يحتفظ بكائن HttpRequest
خارج threadlocal ، فإننا نضع بعض الأشياء الأخرى فيه - على سبيل المثال ، اتصالات قاعدة البيانات أو اللغة الحالية.
يمكن تقسيم استخدام threadlocals إلى خيارين:
- "السكان المحليين للسياق" ، حيث تكون هناك حاجة إلى قيمة في بعض السياق القائم على المكدس ، مثل الطلب. هذا ضروري لتعيين اللغة الحالية.
- "سلاسل الصفحات الحقيقية" ، حيث تكون الشفرة المحمية غير آمنة بالفعل للاتصال من سلسلة رسائل أخرى. هذا للاتصال بقاعدة البيانات.
للوهلة الأولى ، قد يبدو أنه يمكن حل "السكان المحليين للسياق" باستخدام الوحدة النمطية الجديدة لسياقات السياق في بيثون ، ولكن سيظل على جانغو 3.0 دعم بيثون 3.6 ، بينما ظهرت هذه الوحدة في 3.7. بالإضافة إلى ذلك ، contextvars
تصميم السياق على وجه التحديد للتخلص من السياق عند التبديل ، على سبيل المثال ، إلى دفق جديد ، بينما نحتاج إلى حفظ هذه القيم للسماح sync_to_async
و async_to_sync
بالعمل بشكل طبيعي async_to_sync
. عندما يدعم Django فقط 3.7 والإصدارات الأحدث ، قد نفكر في استخدام contextvars
، لكن هذا سيتطلب عملاً كبيراً في Django.
تم حل هذا بالفعل باستخدام asgiref Local
، وهو متوافق مع coroutines وخيوط المناقشة . الآن لا يستخدم contextvars
، ولكن يمكننا contextvars
للعمل مع backport لمدة 3.6 بعد بعض الاختبارات.
من ناحية أخرى ، يمكن أن تستمر مؤشرات الترابط الحقيقية في العمل في سلسلة الرسائل الحالية. ومع ذلك ، يجب أن نكون أكثر حرصًا لمنع تسرب هذه الكائنات إلى دفق آخر ؛ عندما لا يتم تنفيذ العرض التقديمي في نفس مؤشر الترابط ، ولكن يولد مؤشر ترابط لكل مكالمة ORM (أثناء مرحلة "التنفيذ المتزامن ، المجمع غير المتزامن") ، فإن بعض الأشياء التي كانت ممكنة في الوضع المتزامن ستكون مستحيلة في غير متزامن.
سيتطلب ذلك عناية خاصة وحظر بعض العمليات المحتملة سابقًا في الوضع غير المتزامن ؛ الحالات التي نعرفها موصوفة أدناه في أقسام محددة.
دعم في وقت واحد للواجهات متزامن وغير متزامن
إحدى المشكلات الكبيرة التي سنواجهها عند محاولة نقل منفذ Django هي أن Python لا يسمح لك بإنشاء إصدارات متزامنة وغير متزامنة من وظيفة بنفس الاسم.
هذا يعني أنه لا يمكنك فقط أخذ واجهة برمجة التطبيقات (API) وعملها مثل هذا:
هذا قيد مؤسف للطريقة التي يتم بها تنفيذ Python بشكل غير متزامن ، ولا يوجد حل بديل واضح. عندما يتم استدعاء شيء ما ، لا تعرف ما إذا كنت ستنتظر أم لا ، لذلك لا توجد طريقة لتحديد ما يجب إرجاعه.
(ملاحظة: هذا بسبب قيام Python بتنفيذ وظائف غير متزامنة على أنها "استدعاء متزامن يؤدي إلى إرجاع coroutine" ، بدلاً من شيء مثل "استدعاء الأسلوب __acall__
على كائن". لا يوجد لدى مديري السياق غير المتزامن __acall__
هذه المشكلة ، لأن لديهم طرق منفصلة __aiter__
و __aenter__
.)
مع وضع ذلك في الاعتبار ، يجب أن نضع مساحات الأسماء للتطبيقات المتزامنة وغير المتزامنة بشكل منفصل عن بعضها البعض حتى لا تتعارض. يمكننا القيام بذلك باستخدام الوسيطة المسماة sync=True
، لكن هذا يؤدي إلى تداخل بين الدوال / الأساليب ويمنع استخدام async def
، ويسمح لك أيضًا بطريق الخطأ أن تنسى كتابة هذه الوسيطة. مكالمة عشوائية لطريقة متزامنة عندما تريد أن نسميها بشكل غير متزامن أمر خطير.
يتمثل الحل المقترح لمعظم الأماكن في قاعدة كود Django في توفير لاحقة لأسماء التطبيقات غير المتزامنة للوظائف - على سبيل المثال ، cache.get_async
بالإضافة إلى cache.get
متزامن. على الرغم من أن هذا يعد حلًا قبيحًا ، إلا أنه يجعل من السهل جدًا اكتشاف الأخطاء عند عرض التعليمات البرمجية (يجب عليك await
باستخدام طريقة _async
).
المشاهدات ومعالجة HTTP
قد تكون طرق العرض حجر الزاوية في فائدة عدم التزامن ، ونتوقع أن يختار معظم المستخدمين بين الشفرة غير المتزامنة والرمز المتزامن.
سوف يدعم Django نوعين من المشاهدات:
- تمثيلات متزامنة ، يتم تعريفها ، كما هو الحال الآن ، بواسطة دالة أو فئة متزامنة مع
__call__
متزامن - تمثيلات غير متزامنة محددة بواسطة دالة غير متزامنة (إرجاع coroutine) أو فئة مع
__call__
غير متزامن.
ستتم معالجتها بواسطة BaseHandler
، والتي ستتحقق من طريقة العرض التي تم استلامها من محلل عناوين URL وسوف تسميها وفقًا لذلك. يجب أن يكون المعالج الأساسي هو الجزء الأول من Django ليصبح غير متزامن ، وسنحتاج إلى تعديل معالج WSGI للاتصال به في حلقة الحدث الخاصة به باستخدام async_to_sync
.
ATOMIC_REQUESTS
الطبقات الوسيطة (الوسيطة) أو إعدادات مثل ATOMIC_REQUESTS
، التي ATOMIC_REQUESTS
طرق العرض في التعليمات البرمجية غير الآمنة بشكل غير متزامن (على سبيل المثال ، الكتلة atomic()
) ، العمل ، لكن سرعتها ستتأثر (على سبيل المثال ، حظر مكالمات ORM المتوازية داخل العرض باستخدام atomic()
).
سيتم تعديل فئة StreamingHttpResponse
الحالية لتتمكن من قبول إما مكرر متزامن أو غير متزامن ، ومن ثم سيكون تنفيذها الداخلي غير متزامن دائمًا. وبالمثل ل FileResponse
. نظرًا لأن هذا يمثل نقطة تعارض محتملة __iter__
الجهة الخارجية التي تصل مباشرة إلى كائنات الاستجابة ، ما زلنا بحاجة إلى توفير __iter__
متزامن لفترة الانتقال.
سوف يستمر دعم WSGI من قِبل Django إلى أجل غير مسمى ، لكن معالج WSGI سينتقل إلى تشغيل الوسيطات وطرق العرض غير المتزامنة في حلقة الأحداث الخاصة به لمرة واحدة. من المحتمل أن يؤدي ذلك إلى انخفاض طفيف في الأداء ، لكن في التجارب الأولية لم يكن له تأثير كبير.
ستعمل جميع وظائف HTTP غير المتزامنة داخل WSGI ، بما في ذلك الاستطلاعات الطويلة والاستجابات البطيئة ، لكنها ستكون غير فعالة كما هي الآن ، وتتناول سلسلة / عملية لكل اتصال. ستكون خوادم ASGI هي الوحيدة التي يمكنها دعم العديد من الطلبات المتزامنة بكفاءة ، وكذلك التعامل مع بروتوكولات غير HTTP ، مثل WebSocket ، للاستخدام بواسطة ملحقات مثل القنوات .
طبقات وسيطة
بينما يركز القسم السابق بشكل أساسي على مسار الطلب / الاستجابة ، تحتاج الوسيطة إلى قسم منفصل بسبب التعقيد المتأصل في تصميمها الحالي.
يتم ترتيب Django middlewares الآن في شكل مكدس حيث يحصل كل برنامج get_response
على get_response
لتشغيل التالي بترتيب الوسيطة (أو طريقة عرض الوسيطة الأدنى على المكدس). ومع ذلك ، نحتاج إلى الحفاظ على مزيج من البرامج الوسيطة المتزامنة وغير المتزامنة للتوافق مع الإصدارات السابقة ، ولن يتمكن هذان النوعان من الوصول إلى بعضهما البعض محليًا.
وبالتالي ، لضمان عمل البرامج الوسيطة ، سيتعين علينا بدلاً من ذلك تهيئة كل برنامج وسيط باستخدام العنصر النائب get_response ، والذي يعيد التحكم بدلاً من ذلك إلى المعالج ويعالج نقل البيانات بين البرامج الوسيطة وطريقة العرض ، بالإضافة إلى رمي استثناء. بطريقة ما ، ستبدو في نهاية المطاف كبرنامج وسيط في عصر Django 1.0 من وجهة نظر داخلية ، على الرغم من أن واجهة برمجة تطبيقات المستخدم ستبقى كما هي بالطبع.
يمكننا أن نعلن أن البرمجيات الوسيطة المتزامنة عفا عليها الزمن ، لكنني أوصي بعدم القيام بذلك في أي وقت قريب. إذا وصلنا إلى نهاية دورة تقادمهم ، فيمكننا عندئذٍ إعادة تطبيق الوسيطة إلى نموذج مكدس متكرر بحت ، كما هو الآن.
ORM
ORM هو الجزء الأكبر من Django من حيث حجم الرمز والأصعب تحويله إلى غير متزامن.
هذا يرجع إلى حد كبير إلى حقيقة أن برامج تشغيل قاعدة البيانات الأساسية متزامنة حسب التصميم ، وسوف يكون التقدم بطيئًا نحو مجموعة من برامج تشغيل قواعد البيانات الناضجة والموحدة وغير المتزامنة. بدلاً من ذلك ، يجب علينا تصميم مستقبل تكون فيه برامج تشغيل قاعدة البيانات متزامنة في البداية ، ونضع الأساس للمساهمين الذين سيواصلون تطوير برامج التشغيل غير المتزامنة بطريقة تكرارية.
تنقسم مشكلات ORM إلى فئتين رئيسيتين - مؤشرات الترابط والحظر الضمني.
تيارات
المشكلة الرئيسية في ORM هي أن Django مصمم حول كائن connections
عمومي واحد ، والذي يمنحك بطريقة سحرية الاتصال الصحيح لمؤشر الترابط الحالي الخاص بك.
في عالم غير متزامن - حيث تعمل جميع الكوروتينات في نفس الخيط - ليس هذا مزعجًا فحسب ، بل إنه أمر خطير. دون أي أمان إضافي ، فإن المستخدم الذي يصل إلى ORM كالمعتاد يهدد بقطع كائنات الاتصال عن طريق الوصول إليها من عدة أماكن مختلفة.
لحسن الحظ ، تكون كائنات الاتصال محمولة على الأقل بين مؤشرات الترابط ، على الرغم من أنه لا يمكن استدعاءها من خيطين في نفس الوقت. يهتم Django بالفعل بسلامة مؤشر الترابط لبرامج تشغيل قواعد البيانات في رمز ORM ، وبالتالي لدينا مكان لتغيير سلوكه للعمل بشكل صحيح.
سنقوم بتعديل كائن connections
بحيث يفهم كل من coroutines asgiref.local
- إعادة استخدام بعض التعليمات البرمجية من asgiref.local
، ولكن مع إضافة منطق إضافي. ستتم مشاركة الاتصالات في التعليمات البرمجية غير المتزامنة والمتزامنة التي تستدعي بعضها البعض - مع مرور السياق من خلال sync_to_async
و async_to_sync
- وسيتم فرض الكود المتزامن على التنفيذ بالتسلسل في سلسلة sync_to_async
async_to_sync
واحدة ، لذلك لن يعمل هذا في نفس الوقت كسر سلامة الموضوع.
هذا يعني أننا نحتاج إلى حل مثل مدير السياق لفتح وإغلاق اتصال قاعدة البيانات ، مثل atomic()
. سيسمح لنا ذلك بتوفير اتصال ثابت وخيوط لاصقة في هذا السياق ، ويسمح للمستخدمين بإنشاء سياقات متعددة إذا كانوا يريدون فتح اتصالات متعددة. كما أنه يوفر لنا طريقة محتملة للتخلص من connections
العالمية السحرية إذا أردنا تطوير ذلك.
في الوقت الحالي ، لا يوجد لدى Django إدارة دورة حياة اتصال مستقلة عن الإشارات من فئة المعالج ، وبالتالي سنستخدمها لإنشاء "سياقات الاتصال" هذه ومسحها. سيتم أيضًا تحديث الوثائق لتوضيح كيفية التعامل بشكل صحيح مع الاتصالات خارج دورة الطلب / الاستجابة ؛ حتى في التعليمات البرمجية الحالية ، لا يعلم الكثير من المستخدمين أن أي فريق إدارة طويل الأمد يجب أن يتصل بشكل دوري بـ close_old_connections
للعمل بشكل صحيح.
يعني التوافق مع الإصدارات السابقة أننا يجب أن نسمح للمستخدمين بالوصول إلى connections
من أي رمز عشوائي في أي وقت ، لكننا لن نسمح بذلك إلا للرمز المتزامن ؛ سنضمن أن الكود ملفوف في "سياق اتصال" ، إذا كان غير متزامن ، من اليوم الأول.
قد يبدو أنه سيكون من الجيد إضافة transaction.atomic()
بالإضافة إلى transaction.atomic()
وتطلب من المستخدم تشغيل جميع التعليمات البرمجية داخل واحد منهم ، ولكن هذا يمكن أن يؤدي إلى التشويش حول ما يحدث إذا قمت بالإرفاق واحد منهم داخل الآخر.
بدلاً من ذلك ، أقترح إنشاء مدير سياق جديد db.new_connections()
يمكّن هذا السلوك ، ويجعله ينشئ اتصالًا جديدًا كلما تم استدعاؤه ، والسماح db.new_connections()
التعسفي atomic()
داخله.
في كل مرة new_connections()
كتلة new_connections()
، يقوم Django بإعداد سياق جديد مع اتصالات قاعدة بيانات جديدة. متابعة كافة المعاملات التي تم تنفيذها خارج كتلة؛ تعمل أي مكالمات ORM داخل الكتلة مع اتصال جديد بقاعدة البيانات وستشاهد قاعدة البيانات من وجهة النظر هذه. إذا تم تمكين عزل المعاملة في قاعدة البيانات ، كما يتم عادةً بشكل افتراضي ، فهذا يعني أن الاتصالات الجديدة داخل الكتلة قد لا ترى التغييرات التي تم إجراؤها بواسطة أي معاملات غير ملتزم بها خارجها.
بالإضافة إلى ذلك ، يمكن للاتصالات داخل كتلة new_connections
هذه أن تستخدم atomic()
لتشغيل المعاملات الإضافية على هذه الاتصالات الجديدة. يُسمح بأي تداخل لمديري السياق هذين ، ولكن في كل مرة new_connections
استخدام اتصالات جديدة ، يتم تعليق المعاملات المفتوحة مسبقًا ولا تؤثر على مكالمات ORM حتى يتم new_connections
حظر new_connections
الجديد.
مثال على كيفية ظهور واجهة برمجة التطبيقات هذه:
async def get_authors(pattern):
هذا مطول إلى حد ما ، لكن الهدف هو أيضًا إضافة اختصارات عالية المستوى لتمكين هذا السلوك (وتغطية أيضًا الانتقال من asyncio.ensure_future
في Python 3.6 إلى asyncio.create_task
في 3.7).
باستخدام مدير السياق والتدفقات اللزجة في نفس سياق الاتصال ، نضمن أن تكون جميع الشفرات آمنة قدر الإمكان بشكل افتراضي. هناك احتمال أن يتمكن المستخدم من استخدام الاتصال في مؤشر ترابط واحد لاثنين من أجزاء مختلفة من الطلب باستخدام yield
، ولكن هذا ممكن yield
الآن.
أقفال ضمنية
هناك مشكلة أخرى في تصميم ORM الحالي وهي أن عمليات الحظر (المتعلقة بالشبكة) ، ولا سيما الحقول المرتبطة بالقراءة ، تتم مواجهتها في حالات نموذجية.
إذا كنت تأخذ model_instance.related_field
للنموذج ثم model_instance.related_field
بالوصول إلى model_instance.related_field
، model_instance.related_field
Django بتحميل محتويات النموذج المرتبط بشفافية وإعادته إليك. ومع ذلك ، هذا غير ممكن في التعليمات البرمجية غير المتزامنة - يجب ألا يتم تنفيذ التعليمات البرمجية لحظر التعليمات البرمجية في السلسلة الرئيسية ، ولا يوجد وصول غير متزامن إلى السمات.
لحسن الحظ ، فإن Django لديه بالفعل طريقة للخروج من هذا - select_related
، والذي يقوم بتحميل الحقول ذات الصلة مقدمًا ، select_related
prefetch_related
للعلاقات بين كثير إلى كثير. إذا كنت تستخدم ORM بشكل غير متزامن ، فسنحظر أي عمليات حظر ضمنية ، مثل الوصول إلى الخلفية للسمات ، وإرجاع خطأ يشير إلى أنه يجب عليك أولاً استرداد الحقل.
هذا له فائدة إضافية تتمثل في منع التعليمات البرمجية البطيئة التي تنفذ طلبات N في حلقة ، وهذا خطأ شائع للعديد من مبرمجي Django الجدد. يؤدي ذلك إلى رفع حاجز الإدخال ، ولكن تذكر أن Django غير المتزامن سيكون اختياريًا - سيظل المستخدمون قادرين على كتابة التعليمات البرمجية المتزامنة إذا رغبوا في ذلك (وسيتم تشجيع ذلك في البرنامج التعليمي ، حيث إن الكود المتزامن يصعب ارتكابه أخطاء).
QuerySet
، لحسن الحظ ، يمكن بسهولة تنفيذ المولدات غير المتزامنة ودعم بشفافية كل من التزامن والتزامن:
async def view(request): data = [] async for user in User.objects.all(): data.append(await extract_important_info(user)) return await render("template.html", data)
آخر
أجزاء ORM المرتبطة بتغييرات المخطط لن تكون غير متزامنة ؛ يجب أن يتم استدعاؤها فقط من فرق الإدارة. بعض المشاريع تسميها بالفعل في التقديمات ، ولكن هذه ليست فكرة جيدة على أي حال.
قوالب
القوالب الآن متزامنة تمامًا ، والخطة هي تركها بهذه الطريقة في الخطوة الأولى. , , DEP.
, Jinja2 , .
, Django , . Jinja2 , , , .
, render_async
, render
; , , .
Django — _async
- (, get_async
, set_async
).
, API sync_to_async
, BaseCache
.
, thread-safety API , Django, , . , ORM, , .
, , , ModelForm
ORM .
, - clean
save
, , . , , , DEP.
البريد الإلكتروني
Django, . send_mail_async
send_mail
, async
- (, mail_admins
).
Django , - SMTP, . , , , , .
تجريب
, Django .
ASGI- asgiref.testing.ApplicationCommunicator
. assert' .
Django , , . , — , , HTTP event loop, WSGI.
. , , .
, , . async def
@async_to_sync
, , , Django test runner.
asyncio ( loop' , ) , , , DEBUG=True
. — , , .
WebSockets
Django; , Channels , ASGI, .
, Channels, , ASGI.
, , . , .
, . , — .
, , . , ORM, , , .
:
( 3.0)
( 3.1)
- ORM ( )
- ( )
- البريد الإلكتروني
; , . , , , , .
, - ; , , . , , Django, Django async-only .
, , DEP , , , email . DBAPI — , core Python , , PEP, .
, , , . Django , - , -; .
, - . , — , , .
Python — . - Python , , .
Python asyncio
, , . , , , , Django-size .
Django, «»; , , — , Django — .
, . , API , Django - .
, , Django . , Django ORM , , , -.
, , — . - long-poll server-sent events. Django , - .
Django; . , , , .
Django , . ; Django- , , , , , .
, -- Django .
, . « Django», ; , , .
, , , , API Django, , , Python 3, API, Django Python.
Python
Python . Python, , , .
, Django — - Python — , Python, Python . , , , .
, Django, . , Django , .
— , , , , .
, — , , — Django ( , Python ).
Django?
, Django. , Lawrence Journal-World — , , SPA — , , . , , , , .
, Django , - , — — . , , ; , Django , .
, Django . , . , , , — , , .
Django django-developers , , , , DEP.
, , , :
- : master- Django .
- : Django , , , , Django .
- : , , Django, , Django. , , , , .
Channels DEP ; Django , .
, , . DEP , , — Django .
. Django, , , WSGI , event loop , . 10% — , . , .
, , (- , Python ). , Django ; , , , Django master- .
, ( ORM, ..), ; Python.
, , Django, «» . , , , , .
البدائل
, , , .
_async
, , (, django.core.cache.cache.get_async
), :
from django.core.cache_async import cache cache.get("foo")
, ; , , .
, , ; .
Django
- , ; , , , — .
, , , — , .
Channels
, Channels , «» Django. , - , , Django; ORM, HTTP/middleware flow .
asyncio
event loop' Python, asyncio
, Django. await
async
Python event loop .
, asyncio
, ; Django , , . Django ; , , async runtime, , .
Greenlets/Gevent
Gevent, , Python.
, . yield
await
, API, Django, , . , .
, , , . greenlet-safe Django ORM - new-connection-context, .
, . Django « » gevent, , , , .
DEP.
, — , — , ( ).
, , - . Django Fellows ; — , ( ), , , - .
— , Kickstarter migrations
contrib.postgres
, MOSS (Mozilla) Channels . , Django, , .
, . — Python, Django — — . , Django/async, .
HTTP/middleware/view flow, , , , « », .
, , , ( , Fellows, / , Channels, , ), , .
, , , , Django, .
, , , , API.
, , , , HTTP/middleware flow. , API, APM, .
, , Django , , . , ORM , , — , ORM .
DEP , ; Django .
, asgiref , , . Django Django.
Channels , Django, Django.
حق النشر
( ) CC0 1.0 Universal .