أصبحت Node.js الآن عضوًا في مجموعة الأدوات لبناء خدمات تطبيقات الشبكة عالية التزامن. لماذا أصبحت Node.js محبوبة لدى الجمهور؟ ستبدأ هذه المقالة بالمفاهيم الأساسية للعمليات والخيوط والكوروتينات ونماذج الإدخال/الإخراج، وستمنحك مقدمة شاملة عن Node.js ونموذج التزامن.
نطلق على المثيل قيد التشغيل لبرنامج ما اسم العملية، وهي وحدة أساسية لتخصيص الموارد وجدولة نظام التشغيل، وهي تتضمن بشكل عام الأجزاء التالية:
进程表
. تشغل كل عملية进程表项
(يُسمى أيضًا进程控制块
). يحتوي هذا الإدخال على حالة عملية مهمة مثل عداد البرنامج ومؤشر المكدس وتخصيص الذاكرة وحالة الملفات المفتوحة ومعلومات الجدولة. المعلومات للتأكد من أنه بعد تعليق العملية، يمكن لنظام التشغيل إحياء العملية بشكل صحيح.تتميز العملية بالخصائص التالية:
تجدر الإشارة إلى أنه إذا تم تشغيل برنامج مرتين، حتى لو كان نظام التشغيل يمكنه تمكينهما من مشاركة التعليمات البرمجية (أي توجد نسخة واحدة فقط من التعليمات البرمجية في الذاكرة)، فلا يمكن تغيير أن مثيلي البرنامج قيد التشغيل مختلفان حقيقة العمليات.
أثناء تنفيذ العملية، ولأسباب مختلفة مثل الانقطاعات وجدولة وحدة المعالجة المركزية، ستتحول العملية بين الحالات التالية:
من مخطط تبديل حالة العملية أعلاه، يمكننا أن نرى أن العملية يمكن أن تتحول من حالة التشغيل إلى حالة الاستعداد والحالة المحظورة، ولكن يمكن فقط تحويل حالة الاستعداد مباشرة إلى حالة التشغيل وذلك للأسباب التالية:
لحل المشكلات التالية:
فيما يتعلق بالخيوط، نحتاج إلى معرفة النقاط التالية:
الآن بعد أن فهمنا الخصائص الأساسية للخيوط، دعونا نتحدث عن عدة أنواع من الخيوط الشائعة.
ترابط حالة Kernel هي سلاسل رسائل مدعومة مباشرة من قبل نظام التشغيل، وتتمثل ميزاتها الرئيسية في ما يلي:
هي سلاسل رسائل تم إنشاؤها بالكامل في مساحة المستخدم. خصائصها الرئيسية هي كما يلي:
هي عملية خفيفة الوزن (LWP) عبارة عن سلسلة عمليات مستخدم مبنية على النواة وتدعمها. ميزاتها الرئيسية هي كما يلي:
لا يمكن لمساحة المستخدم استخدام سلاسل عمليات kernel إلا من خلال عمليات خفيفة الوزن (LWP). لذلك، فقط من خلال دعم سلاسل عمليات kernel، يمكن أن تكون هناك عملية خفيفة الوزن (LWP).
تتطلب معظم عمليات العمليات خفيفة الوزن (LWP) مساحة وضع المستخدم لبدء استدعاء النظام مكلفة نسبيًا (تتطلب التبديل بين وضع المستخدم ووضع kernel)؛
يجب أن ترتبط كل عملية خفيفة الوزن (LWP) بخيط نواة محدد، لذلك:
يمكنهم الوصول إلى عملياتهم الخاصة وجميع مساحات العناوين المشتركة وموارد النظام.
أعلاه، قدمنا باختصار أنواع الخيوط الشائعة (خيوط حالة kernel، وخيوط حالة المستخدم، والعمليات خفيفة الوزن). لكل منها نطاق تطبيق خاص بها. في الاستخدام الفعلي، يمكنك استخدامها بحرية وفقًا لاحتياجاتك الخاصة الجمع، مثل النماذج المشتركة من واحد إلى واحد ومن متعدد إلى متعدد وغيرها من النماذج نظرًا لضيق المساحة، لن تقدم هذه المقالة الكثير عن هذا الأمر ويمكن للطلاب المهتمين دراستها بأنفسهم.
، المعروف أيضًا باسم Fiber، عبارة عن آلية تشغيل برنامج مبنية على سلاسل رسائل تسمح للمطورين بإدارة جدولة التنفيذ وصيانة الحالة والسلوكيات الأخرى بأنفسهم. ميزاتها الرئيسية هي
في JavaScript، يعد async/await
الذي نستخدمه غالبًا أحد تطبيقات coroutine، مثل المثال التالي:
function updateUserName(id, name) { const user = getUserById(id); user.updateName(name); عودة صحيحة؛ } وظيفة غير متزامنة updateUserNameAsync(id, name) { مستخدم const = انتظار getUserById(id); في انتظار user.updateName(name); عودة صحيحة؛ }
في المثال أعلاه، تسلسل التنفيذ المنطقي ضمن الوظيفتين updateUserName
و updateUserNameAsync
هو:
true
getUserById
قيمة الإرجاع الخاصة بها إلى user
updateName
user
يكمن الاختلاف الرئيسي بين الاثنين في التحكم في الحالة أثناء التشغيل الفعلي:
updateUserName
، يتم تنفيذها بالتسلسل وفقًا للتسلسل المنطقي المذكور أعلاه؛updateUserNameAsync
، ويتم تنفيذها أيضًا بالتسلسل وفقًا لـ التنفيذ المنطقي المذكور أعلاه، ولكن عند مواجهة await
، سيتم تعليق updateUserNameAsync
وحفظ حالة البرنامج الحالية في الموقع المعلق، ولن يتم تنشيط updateUserNameAsync
مرة أخرى حتى يعود جزء البرنامج بعد await
ويستعيد حالة البرنامج قبل التعليق. ثم تابع إلى البرنامج التالي.من التحليل أعلاه، يمكننا أن نخمن بجرأة: ما تحتاج coroutines إلى حله ليس مشاكل توافق البرنامج التي تحتاج العمليات والخيوط إلى حلها، ولكن المشكلات التي تتم مواجهتها عند معالجة المهام غير المتزامنة (مثل عمليات الملفات، وطلبات الشبكة، وما إلى ذلك)؛ في قبل async/await
، لم نتمكن من التعامل مع المهام غير المتزامنة إلا من خلال وظائف رد الاتصال، مما قد يجعلنا نقع بسهولة في回调地狱
وينتج فوضى من التعليمات البرمجية التي يصعب الحفاظ عليها بشكل عام، من خلال coroutines، يمكننا تحقيق تزامن التعليمات البرمجية غير المتزامنة .
ما يجب أن نأخذه في الاعتبار هو أن القدرة الأساسية للكوروتينات هي القدرة على تعليق برنامج معين والحفاظ على حالة وضع تعليق البرنامج، واستئنافه في وضع التعليق في وقت ما في المستقبل، والاستمرار في ذلك. تنفيذ الجزء التالي بعد وضع التعليق.
تحتاج عملية I/O
الكاملة إلى المرور بالمراحل التالية:
I/O
إلى النواة من خلال استدعاء النظامI/O
في مرحلة الإعداد ومرحلة التنفيذ الفعلي)، ويعيد نتائج المعالجة إلى مؤشر ترابط المستخدم.يمكننا تقسيم عمليات I/O
تقريبًا إلى أربعة أنواع:阻塞I/O
،非阻塞I/O
، وعمليات同步I/O
، وعمليات异步I/O
المفاهيم (هنا نفترض أن الخدمة A تستدعي الخدمة B):
阻塞/非阻塞
:
阻塞调用
非阻塞调用
.同步/异步
:
同步
؛回调
بعد اكتمال التنفيذ. يتم إخطار النتيجة إلى A، ثم تكون الخدمة B异步
.غالبًا ما يخلط العديد من الأشخاص بين阻塞/非阻塞
同步/异步
، لذلك يجب إيلاء اهتمام خاص:
阻塞/非阻塞
مخصص调用者
بالخدمة؛ أما同步/异步
فهو مخصص被调用者
بالخدمة.بعد فهم阻塞/非阻塞
同步/异步
، دعونا نلقي نظرة على I/O 模型
المحدد.
: بعد أن يبدأ مؤشر ترابط المستخدم استدعاء نظام I/O
، سيتم阻塞
مؤشر ترابط المستخدم على الفور حتى تتم معالجة عملية I/O
بأكملها ويتم إرجاع النتيجة إلى مؤشر ترابط المستخدم فقط بعد دخول المستخدم (الخيط) يمكن تحرير حالة阻塞
والاستمرار في تنفيذ العمليات اللاحقة.
الميزات:
I/O
، لا يمكن لعملية (الخيط) الخاصة بالمستخدم إجراء عمليات أخرى؛I/O
واحد يمكن أن يحظر مؤشر ترابط (مؤشر ترابط) وارد، لذلك من أجل الاستجابة لطلب I/O
في الوقت المناسب، من الضروري تخصيص مؤشر ترابط وارد (مؤشر ترابط) لكل طلب، مما سيؤدي إلى موارد ضخمة الاستخدام وطلبات الاتصال الطويلة، حيث لا يمكن تحرير الموارد الواردة (مؤشر الترابط) لفترة طويلة، إذا كانت هناك طلبات جديدة في المستقبل، فسيحدث اختناق خطير في الأداء.تعريف
I/O
في مؤشر ترابط (مؤشر ترابط)، إذا كانت عملية I/O
غير جاهزة، فسيقوم استدعاء I/O
بإرجاع خطأ، ولن يقوم المستخدم بذلك تحتاج إلى إدخال مؤشر الترابط (مؤشر الترابط). انتظر، ولكن استخدم الاستقصاء لاكتشاف ما إذا كانت عملية I/O
جاهزة أم لاI/O
الفعلية مؤشر ترابط المستخدم حتى يتم إرجاع نتيجة التنفيذ إلى ملف موضوع المستخدم.الميزات:
I/O
(عادةً ما يتم ذلك باستخدام حلقة while
)، يحتاج النموذج إلى شغل وحدة المعالجة المركزية واستهلاك موارد وحدة المعالجة المركزيةI/O
جاهزةI/O
I/O
الفعلية اللاحقة المستخدم من دخول مؤشر الترابط (الخيط) ؛بعد أن تبدأ عملية المستخدم (مؤشر الترابط) استدعاء نظام I/O
، إذا تسبب استدعاء I/O
في حظر عملية المستخدم (مؤشر الترابط)، فإن استدعاء I/O
يكون同步I/O
فهو异步I/O
.
معيار الحكم على ما إذا كانت عملية I/O
同步
أم异步
هو آلية الاتصال بين سلاسل العمليات الخاصة بالمستخدم وعمليات I/O
. في
同步
، تتم مزامنة التفاعل بين سلاسل العمليات الخاصة بالمستخدم وعمليات I/O
من خلال المخزن المؤقت للنواة. أي أن النواة ستقوم بمزامنة نتائج تنفيذ عملية I/O
مع المخزن المؤقت، ثم تقوم بنسخ البيانات الموجودة في المخزن المؤقت إلى مؤشر ترابط المستخدم. ستحظر هذه العملية مؤشر ترابط المستخدم حتى تكتمل عملية I/O
异步
في المواقف، تتم مزامنة التفاعل بين مؤشر ترابط المستخدم (مؤشر الترابط) والإدخال I/O
مباشرة من خلال kernel، أي أن النواة ستقوم مباشرة بنسخ نتائج تنفيذ عملية I/O
إلى مؤشر ترابط المستخدم (مؤشر الترابط). عدم حظر عملية (الخيط) الخاصة بالمستخدم.Node.js نموذج إدخال I/O
غير متزامن يعتمد على الحدث، شخصيًا، أعتقد أن سبب اختيار هذا النموذج هو:
I/O
مكثفة. إن كيفية إدارة الموارد متعددة الخيوط بشكل معقول وفعال مع ضمان التزامن العالي أمر أكثر تعقيدًا من إدارة الموارد ذات الخيط الواحد.باختصار، لغرض البساطة والكفاءة، تتبنى Node.js نموذج I/O
غير متزامن أحادي الخيط يحركه الحدث، وتنفذ نموذجها من خلال EventLoop للخيط الرئيسي وخيط العامل المساعد:
تجدر الإشارة إلى أن Node.js غير مناسب لأداء المهام التي تتطلب الكثير من العمليات الحسابية على وحدة المعالجة المركزية (CPU)؛ وذلك لأن كود EventLoop وJavaScript (رمز مهمة حدث غير متزامن) يعملان في نفس الموضوع (أي، الخيط الرئيسي)، وأي منها إذا تم تشغيله لفترة طويلة جدًا، فقد يتسبب ذلك في حظر الخيط الرئيسي. إذا كان التطبيق يحتوي على عدد كبير من المهام التي تتطلب تنفيذًا طويلًا، فسيؤدي ذلك إلى تقليل إنتاجية الخادم وربما حتى يتسبب في عدم استجابة الخادم.
Node.js هي تقنية يجب على مطوري الواجهة الأمامية مواجهتها الآن وحتى في المستقبل، ومع ذلك، فإن معظم مطوري الواجهة الأمامية لديهم معرفة سطحية فقط بـ Node.js من أجل السماح للجميع بفهم نموذج التزامن الخاص بـ Node .js، تقدم هذه المقالة أولاً العمليات والخيوط والروتينات، ثم تقدم نماذج مختلفة I/O
، وأخيرًا تقدم مقدمة مختصرة عن نموذج التزامن الخاص بـ Node.js. على الرغم من عدم وجود مساحة كبيرة لتقديم نموذج التزامن Node.js، يعتقد المؤلف أنه لا يمكن فصله أبدًا عن المبادئ الأساسية. إن إتقان الأساسيات ذات الصلة ومن ثم الفهم العميق لتصميم وتنفيذ Node.js سيؤدي إلى نتيجة مضاعفة بنصف الجهد.
أخيرًا، إذا كانت هناك أي أخطاء في هذه المقالة، آمل أن تتمكن من تصحيحها وأتمنى لكم جميعًا برمجة سعيدة كل يوم.