RPC و gRPC
RPC وgRPC
عندما تحتاج خدمتان تعملان على جهازين مختلفين إلى التواصل، يمكنهما تبادل JSON الخام عبر HTTP، أو استخدام تجريد أعلى مستوى يُعرف بـ الاستدعاء عن بُعد (RPC). يجعل RPC استدعاء دالة على خادم بعيد بسيطاً مثل استدعاء دالة محلية في كودك. أبرز تطبيق حديث لهذا النمط هو gRPC، وهو إطار عمل مفتوح المصدر طورته Google ويُشغّل أنظمتها الداخلية الضخمة — من بينها Google Search وYouTube وواجهة Google Cloud البرمجية — إضافةً إلى عدد لا يحصى من معماريات الخدمات الصغيرة حول العالم.
من الاستدعاءات المحلية إلى البعيدة
في الخدمة المتجانسة (Monolith)، تستدعي وحدة أخرى مباشرةً في الذاكرة. الاستدعاء شبه فوري، وآمن من حيث الأنواع بفضل اللغة، والمترجم يكتشف الأخطاء مبكراً. حين تُفكّك تلك الخدمة إلى ميكروسيرفيسز، يعبر الاستدعاء حدود الشبكة. فجأة يواجهك: الكمون (ميلي ثوانٍ بدل نانو ثوانٍ)، والفشل الجزئي (قد تكون المخدومة غير متاحة)، والحاجة إلى صيغة تسلسل يفهمها الطرفان.
أنظمة RPC الأولى في الثمانينيات (Sun RPC وCORBA وDCOM) حاولت إخفاء كل هذا خلف stubs شفافة — ولّدت كوداً يجعل الاستدعاءات البعيدة تبدو محلية. المشكلة: أفرطت في الإخفاء. كتب المطورون كوداً كأن الشبكة غير موجودة، ثم صُدموا بأوقات الانتهاء وإعادة المحاولات. أنظمة RPC الحديثة أكثر صدقاً: لا تزال تولّد stub code، لكنها تُبرز مخاوف الشبكة صراحةً عبر رموز الخطأ والمهل والـ streaming APIs.
ما هو gRPC
يرتكز gRPC (اختصار Google Remote Procedure Call) على ثلاثة أسس:
- Protocol Buffers (protobuf) — صيغة تسلسل ثنائية ولغة تعريف واجهة (IDL). تصف هياكل بياناتك وتوابع خدماتك في ملف
.proto؛ يقوم المترجمprotocبتوليد كود العميل والخادم بلغتك المفضلة (Go وJava وPython وC++ وNode.js وDart والمزيد). - HTTP/2 — طبقة النقل. يدعم HTTP/2 تدفقات متعددة (طلبات كثيرة عبر اتصال TCP واحد) وضغط الترويسات والـ streaming ثنائي الاتجاه، ويستثمر gRPC كل هذه الإمكانات.
- الـ stubs المولّدة — يُنتج المترجم stub للعميل وهيكلاً للخادم. يستدعي العميل تابعاً على كائن الـ stub؛ يُسلسل الـ stub المعطيات ويرسلها عبر HTTP/2 ثم يُفك تسلسل الاستجابة. يُطبّق الخادم واجهة الهيكل المولّدة.
Protocol Buffers: محرك التسلسل
تُعد Protocol Buffers مفتاح ميزة أداء gRPC على REST+JSON. تُرمَّز رسالة protobuf كتدفق ثنائي مضغوط باستخدام أرقام الحقول بدلاً من أسمائها. لنأخذ مثال البحث عن مستخدم:
البيانات ذاتها التي قد تكلف 120 بايت بصيغة JSON ({"id":42,"username":"alice","email":"alice@example.com","age":30}) تكلف نحو 40 بايت فقط بصيغة protobuf — تخفيض بمقدار 3 أضعاف. عند ملايين الاستدعاءات في الثانية، يُحدث هذا فرقاً كبيراً في الكمون وتكاليف النطاق الترددي. يفرض protobuf أيضاً مخططاً (Schema): يجب أن يتفق المُنتج والمستهلك على أنواع الحقول، مما يُزيل فئة كاملة من أخطاء وقت التشغيل الشائعة في JSON APIs.
أنماط التواصل الأربعة
يدعم gRPC أربعة أنماط تفاعل، جميعها مُعرَّفة في ملف .proto ذاته:
- Unary RPC — طلب واحد واستجابة واحدة. مكافئ لطلب HTTP الكلاسيكي. الأكثر شيوعاً.
- Server Streaming — طلب واحد وتدفق من الاستجابات. مناسب لمجموعات النتائج الكبيرة أو الخلاصات الحية.
- Client Streaming — تدفق من الطلبات واستجابة واحدة. مناسب للرفع الجماعي أو التجميع.
- Bidirectional Streaming — كلا الطرفين يبثّان بشكل متزامن. مناسب للتعاون الفوري والدردشة وقياسات الاستشعار.
gRPC مقابل REST: مقارنة عملية
المقارنة ليست "أيهما أفضل" بل "أيهما يناسب السياق". إليك كيف يختلفان على الأبعاد الأكثر أهمية في تصميم الأنظمة:
- الأداء: يمنح gRPC بفضل التسلسل الثنائي وتعدد تدفقات HTTP/2 ميزة إنتاجية ثابتة تتراوح بين 5 و10 أضعاف مقارنةً بـ REST+JSON على نفس الأجهزة. تُظهر مقاييس Google الداخلية كموناً عند ذيل التوزيع (p99) دون المللي ثانية للحمولات الصغيرة (أقل من 1 كيلوبايت). أما REST فتحليل JSON أبطأ لاسيما في اللغات التي تفتقر إلى معالجة ثنائية أصلية.
- المخطط والعقود: ملفات
.protoفي gRPC هي العقد — يُولَّد كود العميل والخادم من المصدر ذاته. تعتمد REST APIs على OpenAPI/Swagger للتوثيق، لكن ذلك لا يمنع التناقض بين المواصفات والتطبيق الفعلي. في protobuf، تناقض الأنواع خطأ وقت الترجمة لا مفاجأة وقت التشغيل في الساعة الثانية صباحاً. - دعم المتصفح: تعمل REST أصلياً في كل متصفح. أما gRPC فلا — مقطورات HTTP/2 (المستخدمة لرموز حالة gRPC) غير متاحة عبر Fetch API. تحتاج إلى gRPC-Web (بروكسي ترجمة) أو gRPC-Gateway (طبقة تحويل REST مولّدة) لتعريض خدمات gRPC للمتصفحات.
- قابلية القراءة البشرية: استدعاء REST JSON قابل للقراءة بسهولة عبر
curl. استدعاء gRPC تدفق ثنائي — تحتاجgrpcurlأو أداة مخصصة. هذا يُبطئ التنقيح والتكامل مع الأطراف الثالثة في الـ public APIs. - النظام البيئي والأدوات: لدى REST عقود من الأدوات — بوابات API وموازنات حمل وذاكرات مؤقتة ومنتجات أمنية تتحدث HTTP/1.1+JSON أصلاً. يتحسن دعم gRPC باستمرار لكنه يتطلب خيارات بنية تحتية أكثر عمداً.
كيف يعمل gRPC: دورة حياة الطلب
تتبّع استدعاء unary واحد يُرسّخ آلية العمل:
- يستدعي كود العميل تابعاً على الـ stub المولّد:
stub.GetUser(request). - يُسلسل الـ stub رسالة
GetUserRequestإلى مصفوفة بايتات ثنائية. - يفتح وقت تشغيل gRPC (أو يُعيد استخدام) تدفق HTTP/2 إلى الخادم على المنفذ 443 (أو 50051 للخدمات الداخلية). كل استدعاء RPC يحصل على معرّف تدفق خاص به، مما يجعل عدة استدعاءات قيد التنفيذ تتشارك اتصال TCP واحد دون حجب رأس الطابور.
- يستقبل الخادم البايتات، يُفكّك الهيكل المولّد تسلسلها إلى كائن
GetUserRequest، ويستدعي المعالج. - يبني المعالج كائن
UserResponse، يُسلسله الهيكل ويُرسله على نفس تدفق HTTP/2. - يُغلق التدفق بـ مقطورة HTTP/2 تحمل رمز حالة gRPC (
OKأوNOT_FOUNDأوUNAVAILABLEوغيرها).
تطور المخطط: إضافة حقول بأمان
الأنظمة الحقيقية تتطور. يتعامل protobuf مع تغييرات المخطط بمرونة بفضل نظام أرقام الحقول. القواعد:
- إضافة حقل جديد آمنة دائماً — العملاء القدامى يتجاهلون أرقام الحقول غير المعروفة؛ الخوادم الجديدة ترسل الحقل الجديد والعملاء القدامى يتجاهلونه.
- حذف حقل يتطلب حجز رقمه لمنع إعادة استخدامه:
reserved 3;. إذا أعدت استخدام رقم قديم لنوع مختلف، سيُسيء الكود القديم والجديد تفسير بايتات بعضهما. - إعادة تسمية حقل آمنة — protobuf يتجاهل الأسماء على الشبكة؛ أرقام الحقول وحدها تهم.
- تغيير نوع حقل خطر ويكسر التوافق في الغالب.
الاستخدام الحقيقي على نطاق واسع
تستخدم Google مستودعاً موحّداً لملفات .proto يُعرّف جميع عقود الخدمات الداخلية. عند تعديل أي فريق لملف .proto، يُعيد نظام CI توليد وإعادة ترجمة جميع الخدمات المعتمدة تلقائياً، مما يكتشف الكسر في وقت البناء لا في الإنتاج. تستخدم Netflix وUber وLyft وSquare وDropbox gRPC بكثافة في شبكات الخدمات الداخلية. تستخدمه طبقة التحكم في Kubernetes، ويتحدث Envoy Proxy مع gRPC أصلاً وهو الـ sidecar المهيمن في شبكات الخدمات مثل Istio وLinkerd.
على مقياس Google (~10 مليارات استدعاء RPC في الثانية داخلياً)، حتى تخفيض 10% في عبء التسلسل يُترجم مباشرةً إلى آلاف الخوادم المُزالة من البنية التحتية — وهو السبب الذي دفع Google للاستثمار بكثافة في protobuf وHTTP/2.
.proto واحد يولّد stubs بكل لغة تستخدمها المؤسسة)، والـ APIs الداخلية عالية الإنتاجية (معالجة الطلبات ومحركات التوصيات وخدمة الإعلانات)، وحالات استخدام الـ streaming ثنائي الاتجاه (تدفقات القياسات والتحرير التعاوني ومزامنة حالة اللعبة الحية).