اذهب إلى المحتوى

النمط الشائع للاتصال الذي تستخدمه برامج التطبيقات المُهيكَلة كزوج عميل / خادم client/server هو اتفاق رسائل الطلب / الرد request/reply message: يرسل العميل رسالة طلبٍ إلى الخادم، ويستجيب الخادم برسالة رد، مع تعطيل العميل (تعليق التنفيذ) انتظارًا للرد. يوضح الشكل التالي التفاعل الأساسي بين العميل والخادم في مثل هذا التبادل.

TimelineForRPC.png

إنّ بروتوكول النقل الذي يدعم نموذج الطلب / الرد أكثر بكثير من رسالة UDP تسير في اتجاه واحد متبوعةً برسالة UDP تذهب في الاتجاه الآخر، فهو يحتاج إلى التعامل مع تحديد العمليات بصورةٍ صحيحة على المضيفين البعيدين وربط الطلبات بالاستجابات. قد يحتاج أيضًا إلى التغلب على بعض أو كل قيود الشبكة الأساسية الموضحة في بيان المشكلة في بداية هذا المقال. يتغلب بروتوكول TCP على هذه القيود من خلال توفير خدمة تدفق بايتات موثوقة، ولكنه لا يتطابق تمامًا مع نموذج الطلب / الرد. يصف هذا القسم فئةً ثالثةً من بروتوكول النقل، تُسمى استدعاء الإجراء عن بُعد Remote Procedure Call أو اختصارًا RPC، والتي تتطابق بشكل وثيق مع احتياجات التطبيق المُضمَّنة في تبادل رسائل الطلب / الرد.

أساسيات الإجراء عن بعد RPC

بروتوكول RPC ليس بروتوكولًا تقنيًا، فمن الأفضل التفكير به كآلية عامة لبناء الأنظمة الموزعة. يُعَد RPC شائعًا لأنه يعتمد على دلالات استدعاء الإجراء المحلي، حيث يستدعي برنامج التطبيق إجراءً دون تحديد ما إذا كان محليًا أو بعيدًا ويتوقف حتى يعيد الإجراء قيمة. يمكن أن يكون مطور التطبيق غير مدرك إلى حد كبير ما إذا كان الإجراء محليًا أم بعيدًا، مما يبسط مهمته إلى حدٍ كبير. يُعرف RPC باسم استدعاء الطرائق عن بُعد remote method invocation أو اختصارًا RMI عندما تكون الإجراءات التي يتم استدعاؤها هي في الواقع طرائق methods لكائناتٍ بعيدة في لغة موجّهة بالكائنات. على الرغم من أن مفهوم RPC بسيط، إلا أن هناك مشكلتين رئيسيتين تجعله أعقد من استدعاءات الإجراءات المحلية:

  • تملك الشبكة بين العملية المستدعية calling process والعملية المُستدعاة called process خصائصًا أعقد من اللوحة الأم المعززة backplane للحاسوب، فمن المحتمل أن تحد من أحجام الرسائل وتميل إلى فقدان الرسائل وإعادة ترتيبها على سبيل المثال.
  • قد تحتوي الحواسيب التي تعمل عليها العمليات المستدعية والمُستدعاة على معماريات وتنسيقات تمثيل بيانات مختلفة.

وبالتالي تتضمن آلية RPC الكاملة في الواقع مكونين رئيسيين:

  • بروتوكول يدير الرسائل المرسلة بين عمليات العميل وعمليات الخادم ويتعامل مع الخصائص غير المرغوب فيها المحتملة للشبكة الأساسية.
  • دعم لغة برمجة ومصرّف compiler لحزم الوسطاء arguments في رسالة طلب على جهاز العميل ثم ترجمة هذه الرسالة مرة أخرى إلى الوسطاء على جهاز الخادم، وبالمثل مع القيمة المُعادة. يُطلق على هذه القطعة من آلية RPC عادةً اسم مصرّف جذعي stub compiler.

يوضح الشكل الآتي ما يحدث عندما يستدعي العميل إجراءً عن بُعد. أولًا، يستدعي العميل جذعًا stub محليًا للإجراء، ويمرر إليه الوسطاء التي يتطلبها هذا الإجراء. يخفي هذا الجذع حقيقة أن الإجراء بعيد عن طريق ترجمة الوسطاء إلى رسالة طلب ثم استدعاء بروتوكول RPC لإرسال رسالة الطلب إلى جهاز الخادم. يسلّم بروتوكول RPC رسالة الطلب في الخادم إلى جذع الخادم، والذي يترجمه إلى وسطاء لهذا الإجراء ثم يستدعي الإجراء المحلي. يعود إجراء الخادم بعد اكتماله في رسالة رد تُفيد بأنه يسلّم بروتوكول RPC لإرساله مرة أخرى إلى العميل. يمرر بروتوكول RPC الموجود على العميل هذه الرسالة إلى جذع العميل، والذي يترجمه إلى قيمة معادة إلى برنامج العميل.

CompleteRPCMechanism.png

يتناول هذا القسم الجوانب المتعلقة بالبروتوكول فقط لآلية RPC، أي أنه يتجاهل الأجزاء الجذعية stubs ويركز بدلًا من ذلك على بروتوكول RPC، الذي يشار إليه أحيانًا باسم بروتوكول الطلب / الرد، الذي ينقل الرسائل بين العميل والخادم. من المهم أيضًا أن تضع في حساباتك أن برامج العميل والخادم مكتوبة في بعض لغات البرمجة، مما يعني أن آلية RPC المعينة قد تدعم Python stubs وJava stubs وGoLang stubs وما إلى ذلك، يتضمن كل منها مصطلحات خاصة باللغة عن كيفية استدعاء الإجراءات.

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

المعرفات Identifiers في RPC

يجب تأدية وظيفتين بواسطة أي بروتوكول RPC هما:

  • وفّر مساحة اسم name space لتعريف الإجراء الذي سيُستدعى بشكل فريد.
  • طابق كل رسالة رد برسالة الطلب المقابلة.

المشكلة الأولى لها بعض أوجه التشابه مع مشكلة تحديد العقد في الشبكة، وهو شيء تفعله عناوين IP على سبيل المثال. أحد خيارات التصميم عند تحديد الأشياء هو جعل مساحة الاسم مسطحة flat أو هرمية hierarchical. تسند مساحة الاسم المسطحة ببساطة معرّفًا فريدَا غير منظم (عددًا صحيحًا مثلًا) لكل إجراء، وسيُنقَل هذا الرقم في حقلٍ واحد في رسالة طلب RPC. قد يتطلب ذلك نوعًا من التنسيق المركزي لتجنب إسناد رقم الإجراء نفسه لاثنين من الإجراءات المختلفة. ويمكن للبروتوكول تطبيق مساحة أسماء هرمية، مماثلة لتلك المستخدمة لأسماء مسار الملف، والتي تتطلب فقط أن يكون "الاسم الأساسي basename" لملفٍ ما فريدًا داخل مجلده، ومن المحتمل أن يبسط هذا النهج مهمة ضمان تفرد أسماء الإجراءات. يمكن تطبيق مساحة أسماء هرمية لآلية RPC عن طريق تحديد مجموعة من الحقول في صيغة رسالة الطلب، حقلٌ لكل مستوى من مستويات التسمية في مساحة اسماءٍ هرمية من مستويين أو ثلاثة مستويات على سبيل المثال.

المفتاح لمطابقة رسالة الرد برسالة الطلب المقابلة هو تحديد أزواج الطلبات والردود بشكل فريد باستخدام حقل معرّف الرسالة. حيث يُضبَط حقل معرّف رسالة الرد على نفس قيمة رسالة الطلب. تستخدم وحدة RPC في العميل التي تتلقى الرد معرّفَ الرسالة للبحث عن الطلب المعلّق المقابل. يُوقَف المستدعي حتى استلام رسالة الرد لجعل اتفاق RPC يظهر مثل استدعاء إجراء محلي للمستدعي caller. يُحدَّد المستدعي الذي جرى وقفه عند تلقي الرد بناءً على رقم الطلب في الرد، ويتم الحصول على القيمة المعادة من الإجراء البعيد من خلال الرد reply، ويُلغى وقف المستدعي حتى يتمكن من إعادة هذه القيمة المعادة.

أحد التحديات المتكررة في RPC هو التعامل مع الاستجابات غير المتوقعة، ونرى ذلك مع معرّفات الرسائل. افترض الحالة المرضيّة (ولكن الواقعية) التالية على سبيل المثال: يرسل جهاز العميل رسالة طلب بمعرف رسالة 0، ثم يتعطل ويعيد التشغيل، ثم يرسل رسالة طلب ليست ذات صلة، وأيضًا بمعرّف رسالة 0. ربما لم يكن الخادم على علم بأن العميل قد تعطل وأعيد تشغيله، عند رؤية رسالة طلب بمعرف رسالة 0، فيُقِر بها ثم يتجاهلها كونها مكررة، ولا يتلقى العميل أبدًا ردًا على الطلب.

يوجد طريقةٌ واحدة للتخلص من هذه المشكلة هي استخدام معرّف إقلاع boot ID. معرف إقلاع الجهاز هو رقم يُزاد في كل مرة يعاد تشغيل الجهاز. يُقرَأ هذا الرقم من وحدة تخزين غير متطايرة (محرك أقراص أو محرك أقراص محمول)، فيُزاد ويعاد كتابته إلى جهاز التخزين أثناء إجراء بدء تشغيل الجهاز، ثم يُوضَع هذا الرقم في كل رسالة يرسلها ذلك المضيف. إذا استُلِمت رسالةٌ بمعرّف رسالة قديم ولكن مع معرف إقلاع جديد، سيجري التعرف عليها كرسالة جديدة. يتحد معرّف الرسالة ومعرّف الإقلاع لتشكيل معرّفٍ فريد لكل اتفاق.

التغلب على قيود الشبكة

تؤدي بروتوكولات RPC غالبًا وظائفًا إضافية للتعامل مع حقيقة أن الشبكات ليست قنوات مثالية. اثنان من هذه الوظائف هي:

  • توفير توصيل موثوق للرسائل.
  • دعم أحجام الرسائل الكبيرة من خلال التجزئة fragmentation وإعادة التجميع reassembly.

قد يعرّف بروتوكول RPC "هذه المشكلة بعيدًا" عن طريق اختيار التشغيل فوق بروتوكول موثوق مثل بروتوكول TCP، ولكن يطبّق بروتوكول RCP في كثير من الحالات طبقة تسليم الرسائل الموثوقة الخاصة به فوق ركيزة غير موثوقة مثل UDP / IP، وبالتالي من المحتمل أن يطبّق بروتوكول RPC هذا الوثوقيةَ باستخدام الإشعارات acknowledgments والمهل الزمنية timeouts، على غرار بروتوكول TCP.

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

SimpleTimelineForAReliableRPCProtocol.png

قد تضيع في الشبكة، تلك الرسالة التي تحمل بيانات (رسالة طلب أو رسالة رد) أو إشعار ACK مُرسَل للإقرار بوصول الرسالة، لذلك يحفظ كلٌّ من العميل والخادم نسخةً من كل رسالة يرسلانها حتى وصول إشعار ACK لها. يضبط كل جانب أيضًا مؤقت إعادة إرسال RETRANSMIT أي يعيد إرسال الرسالة في حالة انتهاء صلاحية هذا المؤقت، ويعيد كلا الجانبين ضبط هذا المؤقت ويحاولان مرة أخرى عددًا من المرات متفقًا عليه قبل التخلي عن الرسالة وتحريرها.

إذا تلقى عميل RPC رسالةَ رد، فمن الواضح أنه يجب أن يكون الخادم قد استلم رسالة الطلب المقابلة، حيث أن رسالة الرد نفسها هي إشعارٌ ضمني implicit acknowledgment، ولن يكون أيُّ إشعارٍ إضافي من الخادم ضروريًا منطقيًا. يمكن لرسالة الطلب إرسال إشعارًا ضمنيًا لرسالة الرد السابقة على افتراض أن البروتوكول يجعل اتفاقات الطلب والرد متسلسلة، بحيث يجب إكمال اتفاقٍ واحد قبل أن يبدأ الاتفاق التالي، ولكن سيحد هذا التسلسل بشدة من أداء RPC.

طريقة الخروج من هذا المأزق هي أن يطبّق بروتوكول RPC تجريد القناة channel abstraction. تكون اتفاقات الطلب / الرد متسلسلة داخل قناةٍ معينة، حيث يمكن أن يكون هناك اتفاقٌ واحد فقط نشط على قناة معينة في أي وقت معين، ولكن يمكن أن تكون هناك قنوات متعددة. يتيح تجريد القناة إمكانية تعدد اتفاقات طلب / رد RPC بين زوج العميل / الخادم.

تتضمن كلُّ رسالةٍ حقلَ معرّف القناة للإشارة إلى القناة التي تنتمي إليها الرسالة. ستُقِر رسالة طلب في قناة معينة ضمنيًا بالرد السابق في تلك القناة، إذا لم يكن قد أُقِر بها بالفعل. يمكن لبرنامج التطبيق فتح قنوات متعددة إلى الخادم إذا كان يريد الحصول على أكثر من اتفاق طلب / رد بينها في نفس الوقت، ويحتاج التطبيق عندئذٍ إلى خيوطٍ threads متعددة. تُقِر رسالة الرد برسالة الطلب، ويُقِر الطلب اللاحق بالرد السابق كما هو موضح في الشكل التالي. لاحظ أننا رأينا طريقة مشابهة جدًا، تسمى القنوات المنطقية المتزامنة concurrent logical channels، في قسم سابق كطريقة لتحسين أداء آلية موثوقية التوقف والانتظار.

TimelineForAReliableRPCProtocolUsingImplicitAcknowledgment.png

من التعقيدات الأخرى الواجب على RPC معالجتها أن الخادم قد يستغرق وقتًا طويلًا بصورة كيفية لتحقيق النتيجة، والأسوأ من ذلك، أنه قد يتعطل قبل توليد الرد. ضع في بالك أننا نتحدث عن الفترة الزمنية بعد موافقة الخادم على الطلب ولكن قبل إرسال الرد. يمكن لعميل RPC إرسال رسالة دورية "هل أنت على قيد الحياة؟ ?Are you alive" إلى الخادم لمساعدة العميل على التمييز بين الخادم البطيء والخادم المعطَّل، ويستجيب جانب الخادم بإشعار ACK. ويمكن للخادم إرسال رسائل "ما زلت على قيد الحياة I am still alive" إلى العميل دون أن يطلبها العميل أولًا. هذا النهج أكثر قابلية للتوسع لأنه يضع المزيد من العبء (أي إدارة مؤقت المهلة الزمنية) على العملاء.

قد تتضمن وثوقية RPC الخاصيةَ المعروفة باسم دلالاتٌ لمرة واحدة على الأكثر at-most-once semantics. هذا يعني أنه بالنسبة لكل رسالة طلب يرسلها العميل، تُسلَّم نسخةٌ واحدة على الأكثر من تلك الرسالة إلى الخادم. في كل مرةٍ يستدعي فيها العميلُ إجراءً بعيدًا، يُستدعى هذا الإجراء مرة واحدة على الأكثر على جهاز الخادم. نقول "مرة واحدة على الأكثر" بدلًا من "مرة واحدة تمامًا" لأنه من الممكن دائمًا فشل الشبكة أو جهاز الخادم، مما يجعل من المستحيل تسليم نسخةٍ واحدة من رسالة الطلب.

يجب أن يتعّرف RPC في جانب الخادم على الطلبات المكررة ويتجاهلها لتنفيذ الدلالات مرة واحدة على الأكثر، حتى إذا كان قد استجاب بالفعل للطلب الأصلي بنجاح، ويجب أن يحتفظ ببعض معلومات الحالة التي تحدد الطلبات السابقة. تتمثل إحدى الطرق في تحديد الطلبات باستخدام الأرقام التسلسلية، لذلك يحتاج الخادم فقط إلى تذكر الرقم التسلسلي الأحدث، ولكن قد يحد ذلك من RPC وبالتالي يقتصر على طلبٍ معلق واحد إلى خادم معين في كل مرة، حيث يجب إكمال طلب واحد قبل إرسال الطلب بالرقم التسلسلي التالي. توفر القنوات الحل، فيمكن للخادم التعرّفَ على الطلبات المكررة من خلال تذكر الرقم التسلسلي الحالي لكل قناة، دون حدِّ العميل بطلب واحد في كل مرة.

لا تدعم كل بروتوكولات RPC هذا السلوك، حيث يدعم البعض دلالاتٍ semantics تسمى دلالات الصفر أو أكثر zero-or-more semantics، أي أن كل استدعاء للعميل يؤدي إلى استدعاء الإجراء البعيد صفر مرةً أو أكثر. ليس من الصعب فهم كيف يمكن أن يتسبب ذلك في حدوث مشكلاتٍ لإجراءٍ بعيد قام بتغيير بعض متغيرات الحالة المحلية (زيادة عدّاد مثلًا) أو كان له بعض الآثار الجانبية المرئية خارجيًا (إطلاق صاروخ على سبيل المثال) في كل مرة يُستدعَى فيها. إذا كان الإجراء البعيد المُستدعى عديم الفعالية، سيكون للاستدعاءات المتعددة نفس تأثير استدعاءٍ واحد فقط، إذًا لا تحتاج آلية RPC إلى دعم دلالات مرة واحدة على الأكثر، ويكفي تطبيق أبسط قد يكون أسرع.

يكمن السببان وراء تطبيق بروتوكول RPC تجزئة الرسالة وإعادة تجميعها كما كان الحال مع الوثوقية بعدم توفير مكدس البروتوكول الأساسي لذلك أو أنه يمكن تطبيقه بصورة أكثر كفاءة بواسطة بروتوكول RPC. ضع في اعتبارك الحالة التي يُطبَّق RPC أعلى UDP / IP ويعتمد على IP للتجزئة وإعادة التجميع. إذا فشل جزءٌ واحد فقط من الرسالة في الوصول خلال فترة زمنية معينة، فإن بروتوكول IP يتجاهل الأجزاء التي وصلت بالفعل وستضيع الرسالة فعليًا. ستنتهي مهلة بروتوكول RPC (بافتراض أنه يطبّق الوثوقية) ويعيد إرسال الرسالة. في المقابل، ضع في اعتبارك بروتوكول RPC الذي يطبّق التجزئة وإعادة التجميع الخاصة به ويرسل ACK أو NACK (إشعارًا سلبيًا) بالأجزاء الفردية. ستُكتشَف الأجزاء المفقودة ويُعاد إرسالها بسرعةٍ أكبر، وسيعاد إرسال الأجزاء المفقودة فقط، وليس الرسالة بأكملها.

البروتوكولات المتزامنة مقابل البروتوكولات غير المتزامنة

تتمثل إحدى طرق وصف البروتوكول في كونه متزامنًا synchronous أو غير متزامن asynchronous. يعتمد المعنى الدقيق لهذه المصطلحات على مكان استخدامها في تسلسل البروتوكول الهرمي. من الأدق في طبقة النقل التفكير فيهما على أنهما يعينان الحدود القصوى للطيف بدلًا من عدّهما بديلين متعارضين. الخاصية الرئيسية لأي نقطة على طول الطيف هي مقدار ما تعرفه عملية الإرسال بعد جواب عملية إرسال الرسالة. أي إذا افترضنا أن أحد برامج التطبيق يستدعي العملية send على بروتوكول نقل، فما الذي يعرفه التطبيق بالضبط عن نجاح العملية عند رد أو جواب العملية send؟

لا يعرف التطبيق شيئًا على الإطلاق عند عودة العملية send في الطرف غير المتزامن من الطيف، فهو لا يعرف فقط ما إذا كانت الرسالة قد استقبلها نظيرها، ولكنه لا يعرف أيضًا على وجه اليقين فيما إذا غادرت الرسالة الجهاز المحلي بنجاح أم لا. تعيد عملية send عادةً رسالةَ رد في الطرف المتزامن من الطيف، أي أن التطبيق لا يعرف فقط أن الرسالة التي أرسلها قد استلمها نظيره، ولكنه يعرف أيضًا أن النظير قد أعاد إجابة. وبالتالي تطبّق البروتوكولات المتزامنة تجريد الطلب / الرد، بينما تُستخدَم البروتوكولات غير المتزامنة إذا أراد المرسل أن يكون قادرًا على إرسال العديد من الرسائل دون الحاجة إلى انتظار الرد. تكون بروتوكولات RPC عادة بروتوكولات متزامنة باستخدام التعريف السابق.

على الرغم من أننا لم نناقشها في هذا المقال، إلا أن هناك نقاطًا مثيرة للاهتمام بين هذين النقيضين. قد يطبّق بروتوكول النقل العملية send بحيث تُوقَف (أي لا تعيد قيمة) حتى تُستلَم الرسالة بنجاح على الجهاز البعيد، ولكنها تعيد قيمةً قبل أن يعالجها نظير المرسل على هذا الجهاز البعيد ويستجيب لها بالفعل. يسمى هذا أحيانًا بروتوكول مخطط بيانات موثوق reliable datagram protocol.

تطبيقات وهي SunRPC وDCE وgRP لـ RPC

ننتقل الآن إلى مناقشتنا لبعض أمثلة تطبيقات بروتوكولات RPC. حيث ستُبرز هذه المناقشة بعض قرارات التصميم المختلفة التي اتخذها مصممو البروتوكول. مثالنا الأول هو SunRPC، وهو بروتوكول RPC واسع الاستخدام يُعرف أيضًا باسم Open Network Computing RPC أو اختصارًا ONC RPC. المثال الثاني، الذي سنشير إليه باسم DCE-RPC، هو جزء من بيئة الحوسبة الموزعة Distributed Computing Environment أو اختصارًا DCE. بيئة DCE عبارة عن مجموعة من المعايير والبرمجيات لبناء الأنظمة الموزعة التي حددتها منظمة البرمجيات المفتوحة Open Software Foundation أو اختصارًا OSF، وهي اتحاد من شركات الحاسوب التي تضم في الأصل شركات IBM وDigital Equipment Corporation وHewlett-Packar، ويطلق على OSF اليوم اسم المجموعة المفتوحة Open Group. مثالنا الثالث هو gRPC، وهي آلية RPC شائعة جعلتها شركة Google مفتوحة المصدر، استنادًا إلى آلية RPC التي تستخدمها داخليًا لتطبيق الخدمات السحابية في مراكز البيانات الخاصة بها.

تمثل هذه الأمثلة الثلاثة خيارات تصميم بديلة مثيرة للاهتمام في مساحة حلول RPC، وأقل ما قد تعتقده أنها الخيارات الوحيدة، سنشرح ثلاث آليات أخرى شبيهة بآلية RPC هي WSDL وSOAP وREST في سياق خدمات الويب لاحقًا.

آلية SunRPC

أصبحت آلية SunRPC معيارًا واقعيًا بفضل توزيعها الواسع مع محطات عمل Sun والدور المركزي الذي تلعبه في نظام ملفات الشبكة Network File System أو اختصارًا NFS الشهير في Sun. تبنتها منظمة IETF لاحقًا كَبروتوكول إنترنت قياسي تحت اسم ONC RPC.

يمكن تطبيق آلية SunRPC عبر عدة بروتوكولات نقل مختلفة. ويوضح الشكل التالي الرسم البياني للبروتوكول عند تطبيق SunRPC على UDP. قد يستهجن أحد خبراء الطبقات الصارم فكرة تشغيل بروتوكول نقل عبر بروتوكول نقل، أو يجادل بأن RPC يجب أن يكون شيئًا آخر غير بروتوكول نقل لأنه يظهر "فوق" طبقة النقل. يُعد عمليًا قرار التصميم الخاص بتشغيل RPC على طبقة نقل حالية ذو فحوى ودلالة كبيرين، كما سيتضح في المناقشة التالية:

ProtocolGraphForSunRPCOnTopOfUDP.png

تستخدم آلية SunRPC معرّفات من مستويين لتحديد الإجراءات البعيدة: رقم برنامج ذو 32 بت ورقم إجراء ذو 32 بت (يوجد أيضًا رقم إصدار ذو 32 بت، لكننا نتجاهل ذلك في المناقشة التالية). إذا أُسنِد رقم برنامج x00100003 على سبيل المثال لخادم NFS، ويوجد داخل هذا البرنامج الإجراء getattr هو الإجراء 1، والإجراء setattr هو الإجراء 2، والإجراء read هو الإجراء 6، والإجراء write هو الإجراء 8، وما إلى ذلك. يُرسَل رقم البرنامج ورقم الإجراء في ترويسة رسالة طلب SunRPC، والموضحة حقولها في الشكل التالي. الخادم، الذي قد يدعم عدة أرقام برامج، مسؤولٌ عن استدعاء الإجراء المحدد للبرنامج المحدد. يمثل طلب SunRPC حقًا طلبًا لاستدعاء البرنامج والإجراءات المحددة على الجهاز المعين الذي أُرسِل الطلب إليه، على الرغم من إمكانية تطبيق نفس رقم البرنامج على أجهزةٍ أخرى في نفس الشبكة. وبالتالي فإن عنوان جهاز الخادم (عنوان IP على سبيل المثال) هو طبقة ثالثة ضمنية من عنوان RPC.

SunRPCHeaderFormats.png

قد تنتمي أرقام البرامج المختلفة إلى خوادم مختلفة على نفس الجهاز. تحتوي هذه الخوادم المختلفة على مفاتيح فك تعدد إرسال demux مختلفة لطبقة النقل مثل منافذ UDP، ومعظمها أرقام غير معروفة ولكنها تُسنَد ديناميكيًا، وتسمى مفاتيح تعدد الإرسال هذه محدّدات النقل transport selectors. كيف يمكن لعميل SunRPC الذي يريد التحدث إلى برنامج معين تحديد محدّد النقل الواجب استخدامه للوصول إلى الخادم المقابل؟ الحل هو إسناد عنوانٍ معروف لبرنامجٍ واحد فقط على الجهاز البعيد والسماح لهذا البرنامج بالتعامل مع مهمة إخبار العملاء باستخدام محدّد نقل معين للوصول إلى أي برنامجٍ آخر على الجهاز. يُطلق على الإصدار الأصلي من برنامج SunRPC اسم Port Mapper، وهو يدعم فقط UDP وTCP كبروتوكولات أساسية. رقم هذا البرنامج هو x00100000، ومنفذه المعروف هو 111. يدعم برنامج RPCBIND، المُطوَّر من برنامج Port Mapper، بروتوكولات النقل الأساسية الكيفية. يستدعي كلُّ خادم SunRPC في البداية إجراءَ تسجيل RPCBIND على الجهاز الرئيسي للخادم، لتسجيل محدد النقل وأرقام البرامج التي يدعمها. يمكن للعميل البعيد استدعاء إجراء بحث RPCBIND للبحث lookup عن محدد النقل لرقم برنامجٍ معين.

لجعل هذا أكثر واقعية، نفترض مثالًا باستخدام برنامج Port Mapper مع بروتوكول UDP. لإرسال رسالة طلب إلى إجراء read الخاص ببرنامج NFS، يرسل العميل أولًا رسالة طلب إلى Port Mapper على منفذ UDP المعروف 111، ويطلب العميل هذا الإجراء 3 لربط رقم البرنامج x00100003 مع منفذ UDP حيث يوجد برنامج NFS حاليًا. يرسل العميل بعد ذلك رسالة طلب SunRPC برقم البرنامج x00100003 والإجراء رقم 6 إلى منفذ UDP هذا، وتستدعي وحدة SunRPC عند ذلك المنفذ إجراء read الخاص ببرنامج NFS. يخزّن العميل أيضًا ربط رقم البرنامج إلى المنفذ تخزينًا مؤقتًا بحيث لا يحتاج للرجوع إلى Port Mapper في كل مرة يريد فيها التحدث إلى برنامج NFS. يُعَد NFS، من الناحية العملية، برنامجًا مهمًا لدرجة أنه أُعطي منفذ UDP الخاص به، ولكن نتظاهر بأن الأمر ليس كذلك لأغراض التوضيح.

تشتمل ترويسات رسائل الطلب والرد على حقل XID "معرّف الاتفاق transaction ID"، كما في الشكل السابق، لمطابقة رسالة رد مع الطلب المقابل، بحيث يمكن إرجاع نتيجة استدعاء الإجراء البعيد إلى المستدعي الصحيح. XID هو معرّف الاتفاق الفريد الذي يستخدمه فقط طلبٌ واحد والرد المقابل له. لا يتذكر الخادم المعرّف XID بعد أن نجح في الرد على طلب معين. لا يستطيع SunRPC ضمان دلالات مرة واحدة على الأكثر at-most-once semantics.

تعتمد تفاصيل دلالات SunRPC على بروتوكول النقل الأساسي. لا يطبق SunRPC موثوقيته الخاصة، لذلك لا يمكن الاعتماد عليه إلا إذا كان بروتوكول النقل الأساسي موثوقًا (قد يختار أي تطبيق مُشغَّل عبر SunRPC أيضًا تطبيق آليات الوثوقية الخاصة به فوق مستوى SunRPC). تعتمد القدرة على إرسال رسائل الطلب والرد التي تكون أكبر من وحدة الإرسال القصوى MTU للشبكة على النقل الأساسي. أي لا يحاول SunRPC تحسينَ النقل الأساسي عندما يتعلق الأمر بالموثوقية وحجم الرسالة. بما أن SunRPC يمكن تشغيله عبر العديد من بروتوكولات النقل المختلفة، فإن هذا يمنحه قدرًا كبيرًا من المرونة دون تعقيد تصميم بروتوكول RPC نفسه.

بالعودة إلى صيغة ترويسة SunRPC بالشكل السابق، حيث تحتوي رسالة الطلب على حقول Credentials وVerifier ذات الطول المتغير، وكلاهما يستخدمه العميل بهدف استيثاق authenticate نفسه على الخادم، أي لتقديم دليل على أن العميل لديه الحق في استدعاء الخادم. تُعَد كيفية استيثاق العميل لنفسه على الخادم مشكلة عامة يجب معالجتها بواسطة أي بروتوكول يريد توفير مستوى معقول من الأمن.

آلية DCE-RPC

DCE-RPC هو بروتوكول RPC وهو صميم نظام DCE وكان أساس آلية RPC التي تقوم عليها Microsoft DCOM وActiveX. يمكن استخدامه مع المصرّف الجذعي stub compiler لتمثيل بيانات الشبكة Network Data Representation أو اختصارًا NDR، ولكنه يعمل أيضًا كبروتوكول RPC الأساسي لمعيار Common Object Request Broker Architecture أو اختصارًا CORBA، وهو معيار على مستوى الصناعة لبناء الأنظمة الموزعة والموجَّهة بالكائنات.

يمكن تطبيق DCE-RPC، مثل SunRPC، فوق العديد من بروتوكولات النقل بما في ذلك UDP وTCP. وهو مشابه أيضًا لبروتوكول SunRPC من حيث أنه يحدد مخطط عنونة من مستويين: فك تعدد إرسال بروتوكول النقل إلى الخادم الصحيح، وإرسال DCE-RPC إلى إجراءٍ معين يصدّره هذا الخادم، ويستشير العملاء "خدمة ربط نقاط النهاية endpoint mapping service" (مماثلة لبرنامج Port Mapper الخاص ببروتوكول SunRPC) لمعرفة كيفية الوصول إلى خادمٍ معين. لكن ينفذ DCE-RPC دلالات استدعاء مرة واحدة على الأكثر at-most-once على عكس SunRPC. (يدعم DCE-RPC دلالات استدعاء متعددة، بما في ذلك الدلالات الراسخة المماثلة لدلالات SunRPC، ولكن دلالات at-most-once في معظم الأحيان هو السلوك الافتراضي). هناك بعض الاختلافات الأخرى بين المنهجين، التي سنسلط عليها الضوء في الفقرات التالية.

TypicalDCE-RPCMessageExchange.png

يعطي الشكل السابق خطًا زمنيًا للتبادل النموذجي للرسائل، حيث تُسمَّى كل رسالة بنوع DCE-RPC الخاص بها. يرسل العميل رسالة طلب Request، ويرد الخادم في النهاية برسالة استجابة Response، ويرسل العميل إشعارًا Ack بالاستجابة. يرسل العميل دوريًا رسالة Ping إلى الخادم بدلًا من إقرار الخادم برسائل الطلب، والذي يستجيب برسالة Working للإشارة إلى أن الإجراء البعيد لا يزال قيد التنفيذ. إذا اُستلِم ردُّ الخادم بسرعة معقولة، فلن تُرسَل أية رسائل Ping. تُدعَم أنواع الرسائل الأخرى أيضًا على الرغم من عدم ظهورها في الشكل، حيث يمكن للعميل إرسال رسالة إنهاء Quit إلى الخادم، مطالبًا إياه بإنهاء استدعاءٍ سابق لا يزال قيد التنفيذ، فيستجيب الخادم برسالة Quack أي إشعار بالإنهاء quit acknowledgment. يمكن للخادم أيضًا الرد على رسالة طلب Request برسالة رفض Reject (تشير إلى رفض الاستدعاء)، ويمكنه الرد على رسالة Ping برسالة Nocall (تشير إلى أن الخادم لم يسمع المستدعي مطلقًا).

يحدث كل اتفاق طلب / رد في DCE-RPC ضمن سياق نشاط activity. النشاط عبارة عن قناة طلب / رد منطقية بين زوج من المشاركين، ويمكن أن يكون هناك اتفاق رسالةٍ واحد فقط نشط على قناة معينة في نفس الوقت. يتعين على برامج التطبيق فتح قنوات متعددة إذا كانت تريد الحصول على أكثر من اتفاق طلب / رد فيما بينها في نفس الوقت مثل نهج القناة المنطقية المتزامنة الموصوفة أعلاه. يحدّد حقلُ ActivityId الخاص بالرسالة النشاطَ الذي تنتمي إليه الرسالة، ثم يميز حقل SequenceNum بين الاستدعاءات التي أُجريَت كجزءٍ من نفس النشاط، حيث يؤدّي هذا الحقل نفسَ الغرض من حقل XID الخاص ببروتوكول SunRPC، ولكن يتتبع DCE-RPC الرقم التسلسلي الأخير المستخدَم كجزء من نشاطٍ معين، وذلك لضمان دلالات مرة واحدة على الأكثر بخلاف SunRPC. يستخدم DCE-RPC حقل ServerBoot للاحتفاظ بمعرّف تشغيل الجهاز للتمييز بين الردود المرسلة قبل وبعد إعادة تشغيل جهاز الخادم.

هناك خيار تصميم آخر أُجري في DCE-RPC مختلف عن SunRPC وهو دعم التجزئة وإعادة التجميع في بروتوكول RPC. كما هو مذكور أعلاه، حتى إذا وفّر بروتوكولٌ أساسي مثل IP التجزئة / إعادة التجميع، فإن الخوارزمية الأعقد التي تُطبَّق كجزء من RPC يمكن أن تؤدي إلى انتعاشٍ recovery أسرع وتقليلٍ في استهلاك حيز النطاق التراسلي عند فقد الأجزاء fragments. يعرّف الحقل FragmentNum بشكل فريد كل جزءٍ أو قطعة fragment تشكّل رسالة طلب أو رسالة رد معينة، ويُسنَد رقمُ جزء فريد لكل جزء DCE-RPC مثل (0 و1 و2 و3 وهكذا). يطبّق كلٌّ من العميل والخادم آلية إشعارٍ انتقائية selective acknowledgment، والتي تعمل على النحو التالي عند وصف الآلية بحيث يرسل العميل رسالة طلبٍ مجزأة إلى الخادم، وتُطبَّق نفس الآلية عندما يرسل الخادم استجابة مجزأة إلى العميل.

أولًا، يحتوي كل جزءٍ fragment يشكّل رسالة طلب على FragmentNum فريد ورايةٍ flag تشير إلى ما إذا كانت هذه الرزمة packet هي جزء من استدعاء (frag) أو الجزء الأخير من استدعاء ()call، حيث تحمل رسائلُ الطلب التي تناسب رزمة واحدة رايةً. يعرف الخادم أنه تلقى رسالة الطلب كاملةً عندما يكون لديه الرزمة دون وجود فجوات في أرقام الأجزاء. ثانيًا، يرسل الخادم رسالة Fack، إشعار جزء fragment acknowledgment، إلى العميل استجابةً لكل جزءٍ قادم. يحدّد هذا الإشعار أعلى رقم للجزء الذي استلمه الخادم بنجاح، أي يكون الإشعار تراكميًا، كما هو الحال في بروتوكول TCP. ولكن يُقِر الخادم بصورة انتقائية بأي أرقام أجزاءٍ أعلى تلقاها مخالفةً للترتيب، ويفعل ذلك باستخدام متجّه بتات يحدد هذه الأجزاء المخالفة للترتيب بالنسبة لأعلى جزءٍ من الترتيب تلقاه. أخيرًا، يستجيب العميل بإعادة إرسال الأجزاء المفقودة.

يوضح الشكل التالي كيفية عمل كل ذلك. لنفترض أن الخادم قد تلقى بنجاح أجزاءً fragments حتى الرقم 20، بالإضافة إلى الأجزاء 23 و25 و26. يستجيب الخادم بإشعار Fack الذي يحدد الجزء 20 على أنه أعلى جزء في الترتيب، بالإضافة إلى متجه بتات SelAck مع البت الثالث (23 = 20 + 3)، والخامس (25 = 20 + 5) ، والسادس (26 = 20 + 6) المُشغَّل. يُعطَى حجم المتجه (الذي يقاس بكلماتٍ مؤلفة من 32 بتًا) في حقل SelAckLen من أجل دعم متجه بتاتٍ طويلٍ (تقريبًا) كيفي.

FragmentationWithSelectiveAcknowledgments.png

بما أن DCE-RPC يدعم الرسائل الكبيرة جدًا (إن طول حقل FragmentNum يبلغ 16 بتًا، مما يعني أن بإمكانه دعم 64 ألف جزء)، فليس من المناسب للبروتوكول إطلاق جميع الأجزاء التي تشكّل رسالةً بأسرع ما يمكن بما أن القيام بذلك قد يغمر جهاز الاستقبال. يطبّق DCE-RPC بدلًا من ذلك خوارزميةً للتحكم في التدفق تشبه إلى حدٍ كبير خوارزمية TCP، حيث لا تُقِر كل رسالة Fack بالأجزاء المستلَمة فحسب، بل تُعلِم المرسل أيضًا بعدد الأجزاء التي قد ترسلها الآن. هذا هو الغرض من حقل WindowSize في الشكل السابق، والذي يخدم بالضبط نفس الغرض من حقل AdvertisedWindow الخاص ببروتوكول TCP باستثناء أنه يحسب الأجزاء fragments بدلًا من البايتات. يطبّق DCE-RPC أيضًا آلية للتحكم في الازدحام مشابهة لآلية TCP. قد لا يكون من المستغرب أن تتجنب بعض بروتوكولات RPC ذلك عن طريق تجنب التجزئة نظرًا لتعقيد التحكم في الازدحام.

يوجد لدى المصممين مجموعة كبيرة من الخيارات المفتوحة لهم عند تصميم بروتوكول RPC. تتخذ SunRPC نهجًا أبسط وتضيف القليل نسبيًا إلى النقل الأساسي بخلاف أساسيات تحديد الإجراء الصحيح وتحديد الرسائل. يضيف DCE-RPC المزيد من الوظائف، مع إمكانية تحسين الأداء في بعض البيئات على حساب تعقيد أكبر.

آلية gRPC

على الرغم من أن أصولها من Google، ولكن gRPC لا تمثل Google RPC، فيرمز الحرف "g" إلى شيء مختلف في كل إصدار. رمز الحرف "g" إلى "glamorous" في الإصدار 1.10، وأشار إلى "goose" في الإصدار 1.18.تحظى gRPC بشعبية لأنها تتيح للجميع (كمصدر مفتوح) خبرةَ عشر سنوات داخل Google من خلال استخدام RPC لإنشاء خدمات سحابية قابلة للتوسّع.

هناك بعض الاختلافات الرئيسية بين gRPC والمثالين الآخرين اللذين تناولناهما للتو. الاختلاف الأكبر هو أن gRPC مصممٌ للخدمات السحابية بدلًا من نموذج العميل / الخادم الأبسط الذي سبقه، ففي عالم العميل / الخادم، يستدعي العميل طريقةً في عملية خادم معينة تعمل على جهاز خادم معين. يُفترض أن تكون إحدى عمليات الخادم كافيةً لخدمة الاستدعاءات من جميع عمليات العميل التي قد تستدعيها.

يستدعي العميل باستخدام الخدمات السحابية طريقةً في خدمة service، والتي تُنفَّذ من خلال عددٍ قابلٍ للتوسّع من عمليات الخادم من أجل دعم استدعاءات عدة عملاء كيفيًا في نفس الوقت، ويعمل كلٌّ من هذه العمليات على جهاز خادم مختلف. هذا هو المكان الذي تلعب فيه السحابة: توفّر مراكز البيانات عددًا يبدو لا نهائيًا من أجهزة الخادم المتاحة لتوسيع نطاق الخدمات السحابية. نعني مصطلح "قابل للتوسع scalable" أن عدد عمليات الخادم المتطابقة التي تختار إنشائها يعتمد على ضغط العمل (أي عدد العملاء الذين يريدون الخدمة في أي وقت معين) ويمكن تعديل هذا الرقم ديناميكيًا بمرور الوقت. أحد التفاصيل الأخرى هو أن الخدمات السحابية لا تنشئ عادةً عمليةً جديدة، في حد ذاتها، ولكنها تطلق حاويةً container جديدة، وهي في الأساس عمليةٌ مغلفةٌ داخل بيئة معزولة تتضمن جميع حزم البرمجيات التي تحتاجها العملية لتعمل. تُعَد Docker اليوم المثال الأساسي لمنصة حاويات.

UsingRPCToInvokeAScalableCloudService.png

بالعودة إلى الادعاء القائل بأن الخدمة هي أساسًا مستوىً إضافيًا من التوجّه غير المباشر فوق الخادم، وهذا يعني أن المستدعي يحدد الخدمة التي يريد استدعاءها، ويوجه موازنُ الحِمل load balancer هذا الاستدعاء إلى إحدى عمليات الخوادم العديدة المتاحة (الحاويات) التي تطبّق تلك الخدمة، كما هو موضح في الشكل السابق. يمكن تطبيق موازن الحِمل بطرقٍ مختلفة، بما في ذلك جهاز عتاد، ولكنه يُطبَّق عادةً بواسطة عملية وكيل proxy تُشغَّل في آلة افتراضية (مستضافةٌ أيضًا في سحابة) وليس كجهاز فيزيائي.

هناك مجموعة من أفضل الممارسات لتطبيق شيفرة الخادم الفعلي الذي يستجيب في النهاية لهذا الطلب، وبعض الآليات السحابية الإضافية لإنشاء / إتلاف create/destroy الحاويات وطلبات موازنة الحِمل عبر تلك الحاويات. إن أفضل الممارسات في بناء الخدمات بهذه الطريقة السحابية الأصلية هي: Kubernetes وهو اليوم بمثابة المثال الأساسي لنظام إدارة الحاويات هذا، ومعمارية الخدمات الصغيرة micro-services architecture. كلا الموضوعين مثيران للاهتمام، لكنهما خارج نطاق هذه السلسلة.

ما يهمنا هنا هو بروتوكول النقل في صميم gRPC. هناك خروجٌ كبير عن البروتوكولين السابقين، ليس من حيث المشاكل الأساسية التي تحتاج إلى معالجة، ولكن من حيث نهج gRPC لمعالجة هذه المشاكل. يستعين gRPC بمصادر خارجية للعديد من المشكلات التي تواجه البروتوكولات الأخرى، مما يؤدي إلى حزم gRPC لتلك القدرات في نموذجٍ سهل الاستخدام. دعنا نلقي نظرة على التفاصيل.

أولًا، يعمل gRPC فوق TCP بدلًا من UDP، مما يعني أنه يستعين بمصادر خارجية لمشاكل إدارة الاتصال والنقل الموثوق لرسائل الطلب والرد ذات الحجم الكيفي. ثانيًا، يعمل gRPC فعليًا فوق نسخةٍ مؤمَّنة من بروتوكول TCP تسمى طبقة النقل الآمنة Transport Layer Security أو اختصارًا TLS، وهي طبقة رقيقة تقع فوق بروتوكول TCP في مكدس البروتوكول، مما يعني أنها تستعين بمصادر خارجية لتأمين قناة الاتصال بحيث لا يستطيع الخصوم سرقة أو التنصت على تبادل الرسائل. ثالثًا، يعمل gRPC فعليًا فوق HTTP / 2 (والذي يقع في حد ذاته فوق TCP وTLS)، مما يعني أن لدى مصادر gRPC الخارجية مشكلتان إضافيتان: (1) تشفير / ضغط البيانات الثنائية بكفاءة في رسالة، (2) تعدد إرسال عدة استدعاءات بعيدة من أجل اتصال TCP واحد. يشفّر gRPC المعرّف الخاص بالطريقة البعيدة مثل URI، ومعاملات الطلب للطريقة البعيدة كمحتوىً في رسالة HTTP، والقيمة المعادة من الطريقة البعيدة في استجابة HTTP. يوضّح الشكل التالي مكدس gRPC الكامل، والذي يتضمن أيضًا العناصر الخاصة باللغة. (تتمثل إحدى نقاط قوة gRPC في المجموعة الواسعة من لغات البرمجة التي يدعمها، ويوضح الشكل التالي مجموعة فرعية صغيرة منها فقط).

gRPCCoreStackedOnTopOfHTTPAndTLSAndTCPAndSupportingACollectionOfLanguages.png

نناقش TLS وHTTP في مقالات لاحقة، لكننا نجد أنفسنا في حلقة اعتمادية مثيرة للاهتمام: حيث تُعد آلية RPC تشعبًا عن بروتوكول النقل المستخدَم لتنفيذ التطبيقات الموزعة، ويُعتبر HTTP مثالًا عن بروتوكول مستوى التطبيق، إلّا أنّ gRPC يعمل فوق HTTP وليس العكس.

التفسير المختصر هو أن الطبقات توفر طريقة ملائمة للبشر لفهم الأنظمة المعقدة، ولكن ما نحاول فعله حقًا هو حل مجموعة من المشكلات (مثل النقل الموثوق للرسائل ذات الحجم الكيفي وتحديد المرسلين والمستلمين، تطابق رسائل الطلب مع رسائل الرد، وما إلى ذلك) وإن الطريقة التي تجمَّع فيها هذه الحلول في البروتوكولات، ثم وضع تلك البروتوكولات فوق بعضها البعض هي نتيجةً للتغييرات المتزايدة بمرور الوقت. لو بدأ الإنترنت بآلية RPC موجودةٍ في كل مكان مثل بروتوكول TCP، فربما طُبِّق HTTP فوقها (مثل أغلب البروتوكولات الأخرى على مستوى التطبيق تقريبًا) وربما قضت Google وقتها في تحسين هذا البروتوكول بدلًا من اختراع بروتوكولٍ خاص بها (كما فعلت هي والآخرين مع بروتوكول TCP). لكن ما حدث بدلًا من ذلك أنّ الويب أصبح التطبيق القاتل للإنترنت، مما يعني أن بروتوكول التطبيق الخاص به HTTP أصبح مدعومًا عالميًا من بقية البنية التحتية للإنترنت: جدران الحماية وموازنات الحِمل والتشفير والاستيثاق والضغط وما إلى ذلك. أصبح HTTP بفعالية بروتوكولَ نقل الطلب / الرد العالمي للإنترنت، نظرًا لأن جميع عناصر الشبكة هذه قد صمِّمت للعمل جيدًا مع بروتوكول HTTP.

بالعودة إلى خصائص gRPC الفريدة، فإن أكبر قيمة يقدمها هي دمج التدفق streaming في آلية RPC، أي أن gRPC يدعم أربعة أنماط طلب / رد مختلفة:

  1. RPC البسيط Simple RPC: يرسل العميل رسالة طلبٍ واحدة ويستجيب الخادم برسالة ردٍ واحدة.
  2. Server Streaming RPC: يرسل العميل رسالة طلبٍ واحدة ويستجيب الخادم بتدفقٍ من رسائل الرد. يكمل العميل عمله بمجرد حصوله على جميع استجابات الخادم.
  3. Client Streaming RPC: يرسل العميل تدفقًا من الطلبات إلى الخادم، ويرسل الخادم استجابة واحدة عادةً (ولكن ليس بالضرورة) بعد أن يتلقى جميع طلبات العميل.
  4. تدفق RPC ثنائي الاتجاه Bidirectional Streaming RPC: يبدأ العميل الاستدعاء، ولكن يمكن للعميل والخادم بعد ذلك قراءة وكتابة الطلبات والاستجابات بأي ترتيب، حيث أن التدفقات مستقلةٌ تمامًا عن بعضها.

تعني هذه الحرية الإضافية في كيفية تفاعل العميل والخادم أن بروتوكول نقل gRPC يحتاج إلى إرسال بيانات وصفية metadata ورسائل تحكم إضافية، بالإضافة إلى رسائل الطلب والرد الفعلية، بين النظيرين. تتضمن الأمثلة شيفرات الخطأ Error والحالة Status (للإشارة إلى نجاح أو سبب فشل شيء ما)، وTimeouts (للإشارة إلى المدة التي يرغب العميل انتظارَ الرد فيها) ، وPING (إشعار البقاء نشطًا keep-alive للإشارة إلى أن جانبًا أو آخر لا يزال قيد التشغيل)، وEOS (إشعار نهاية التدفق end-of-stream للإشارة إلى عدم وجود المزيد من الطلبات أو الاستجابات)، وGOAWAY (إشعار من الخوادم للعملاء للإشارة إلى أنهم لن يقبلوا بعد الآن أي تدفقاتٍ جديدة). إن الطريقة التي تُمرَّر فيها معلومات التحكم هذه بين الجانبين يمليها إلى حدٍ كبير بروتوكول النقل الأساسي، الذي هو HTTP / 2 في هذه الحالة، على عكس العديد من البروتوكولات الأخرى في هذه السلسلة، حيث يتضمن HTTP بالفعل مجموعةً من حقول الترويسات وشيفرات الرد التي يستفيد منها gRPC.

قد يتضمن طلب RPC البسيط (بدون تدفق) رسالة HTTP التالية من العميل إلى الخادم:

HEADERS (flags = END_HEADERS)
:method = POST
:scheme = http
:path = /google.pubsub.v2.PublisherService/CreateTopic
:authority = pubsub.googleapis.com
grpc-timeout = 1S
content-type = application/grpc+proto
grpc-encoding = gzip
authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v
DATA (flags = END_STREAM)
<Length-Prefixed Message>

مما يؤدي إلى رسالة الاستجابة التالية من الخادم إلى العميل:

HEADERS (flags = END_HEADERS)
:status = 200
grpc-encoding = gzip
content-type = application/grpc+proto
DATA
<Length-Prefixed Message>
HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK
trace-proto-bin = jher831yy13JHy3hc

تُعَد HEADERS وDATA في هذا المثال رسالتين قياسيتين للتحكم في HTTP، والتي تصف بدقة وفعالية "ترويسة الرسالة" و"حمولة الرسالة". كل سطر يتبع HEADERS (ولكن قبل DATA) هو زوج attribute = value الذي يشكّل الترويسة (فكر في كل سطر على أنه مشابه لحقل الترويسة). الأزواج التي تبدأ بنقطتين (:status = 200 على سبيل المثال) هي جزء من معيار HTTP (تشير الحالة 200 إلى النجاح مثلًا). وتلك الأزواج التي لا تبدأ بنقطتين هي تخصيصات محدّدة بالآلية gRPC (تشير grpc-encoding = gzip على سبيل المثال إلى أن البيانات في الرسالة التالية قد ضُغِطت باستخدام gzip، وتشير grpc-timeout = 1S إلى أن العميل قد ضبط مهلة زمنية مقدارها ثانية واحدة).

هناك قطعة أخيرة لشرحها وهو سطر الترويسة:

content-type = application/grpc+proto

الذي يشير إلى أن جسم الرسالة كما حدّده سطر DATA له معنى فقط لبرنامج التطبيق، أي طريقة الخادم الذي يطلب هذا العميل الخدمة منه. تحدد سلسلة +proto أن المستلم سيكون قادرًا على تفسير البتات في الرسالة وفقًا لمواصفات واجهة مخزَن البروتوكول المؤقت Protocol Buffer أو اختصارًا proto. مخازن البروتوكول المؤقتة هي طريقة gRPC لتحديد كيفية تشفير المعاملات الممرَّرة إلى الخادم في رسالة، والتي تُستخدم بدورها لإنشاء الأجزاء الجذعية التي تقع بين آلية RPC الأساسية والوظائف الفعلية التي تُستدعى.

اقتباس

خلاصة القول هي أن الآليات المعقدة مثل RPC، التي جُمِّعت كحزمةٍ موحَّدةٍ من البرامج كما هو الحال مع SunRPC وDCE-RPC)، التي تُبنى في الوقت الحاضر من خلال تجميع مجموعة متنوعة من القطع الأصغر، ويحل كلٌّ منها مشكلةً. يُعد gRPC مثالًا على هذا النهج، وأداةً تتيح المزيد من الاعتماد على هذا النهج. تطبِّق معماريةُ الخدمات الصغيرة المذكورة سابقًا استراتيجيةَ "البناء من أجزاءٍ صغيرة built from small parts" على تطبيقات السحابة بأكملها، والمتمثلة في كل من Uber وLyft وNetflix وYelp وSpotify على سبيل المثال)، حيث غالبًا ما تكون gRPC هي آلية الاتصال التي تستخدمها تلك الأجزاء الصغيرة لتبادل الرسائل مع بعضها البعض.

ترجمة -وبتصرّف- للقسم Remote Procedure Call من فصل End-to-End Protocols من كتاب Computer Networks: A Systems Approach.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...