ما هو gRPC؟ كيفية العمل مع APISIX؟
September 28, 2022
ما هو gRPC
gRPC هو إطار عمل RPC مفتوح المصدر من Google يهدف إلى توحيد كيفية تواصل الخدمات. يستخدم الإطار بروتوكول HTTP/2 كناقل للبيانات وبروتوكول Buffers كلغة وصف للواجهة. يمكنه توليد الكود تلقائيًا للاتصالات بين الخدمات.
هيمنة gRPC
أصبح gRPC المعيار لإطارات عمل RPC بسبب التأثير الاستثنائي لـ Google على المطورين وبيئات الحوسبة السحابية الأصلية.
هل تريد استدعاء وظائف etcd؟ gRPC!
هل تريد إرسال بيانات OpenCensus؟ gRPC!
هل تريد استخدام RPC في خدمة مصغرة تم تنفيذها بـ Go؟ gRPC!
هيمنة gRPC قوية لدرجة أنه إذا لم تختر gRPC كإطار عمل RPC الخاص بك، فسيتعين عليك تقديم سبب قوي لعدم اختياره. وإلا، سيكون هناك دائمًا من يسأل، لماذا لم تختر gRPC السائد؟ حتى شركة Alibaba، التي قامت بالترويج بقوة لإطار عمل RPC الخاص بها Dubbo، قامت بتعديل تصميم البروتوكول بشكل كبير في الإصدار الأخير من Dubbo 3، حيث تم تغييره إلى نسخة من gRPC متوافقة مع كل من gRPC و Dubbo 2. في الواقع، بدلًا من القول بأن Dubbo 3 هو ترقية من Dubbo 2، يمكن القول بأنه اعتراف بسيادة gRPC.
العديد من الخدمات التي توفر gRPC توفر أيضًا واجهات HTTP المقابلة، ولكن هذه الواجهات غالبًا ما تكون فقط لأغراض التوافق. نسخة gRPC توفر تجربة مستخدم أفضل بكثير. إذا كان بإمكانك الوصول إليها عبر gRPC، يمكنك استيراد SDK المقابل مباشرة. إذا كان بإمكانك فقط استخدام واجهات برمجة تطبيقات HTTP العادية، فعادة ما يتم توجيهك إلى صفحة وثائق، وعليك تنفيذ عمليات HTTP المقابلة بنفسك. على الرغم من أن الوصول عبر HTTP يمكن أن يولد SDK المقابل من خلال مواصفات OpenAPI، إلا أن عددًا قليلاً من المشاريع يأخذ مستخدمي HTTP بجدية كما يفعل مع gRPC، حيث أن HTTP يعتبر أولوية منخفضة.
هل يجب علي استخدام gRPC
APISIX يستخدم etcd كمركز للتكوين. منذ الإصدار v3، قام etcd بنقل واجهته إلى gRPC. ومع ذلك، لا يوجد مشروع يدعم gRPC في نظام OpenResty، لذا فإن APISIX يمكنه فقط استدعاء واجهات برمجة تطبيقات HTTP الخاصة بـ etcd. يتم توفير واجهات برمجة تطبيقات HTTP الخاصة بـ etcd من خلال gRPC-gateway. في الأساس، يقوم etcd بتشغيل وكيل HTTP إلى gRPC على جانب الخادم، ثم يتم تحويل طلبات HTTP الخارجية إلى طلبات gRPC من خلال gRPC-gateway. بعد نشر هذه الطريقة للتواصل لعدة سنوات، وجدنا بعض المشاكل في التفاعل بين واجهة برمجة تطبيقات HTTP وواجهة برمجة تطبيقات gRPC. وجود gRPC-gateway لا يعني أن الوصول عبر HTTP مدعوم بشكل مثالي. لا تزال هناك اختلافات دقيقة.
فيما يلي قائمة بالمشاكل ذات الصلة التي واجهناها مع etcd على مدى السنوات القليلة الماضية:
- gRPC-gateway معطل بشكل افتراضي. بسبب إهمال المسؤول، فإن التكوين الافتراضي لـ etcd لا يقوم بتمكين gRPC-gateway في بعض المشاريع. لذا كان علينا إضافة تعليمات في الوثائق للتحقق مما إذا كان etcd الحالي لديه gRPC-gateway ممكّنًا. انظر https://github.com/apache/apisix/pull/2940.
- بشكل افتراضي، يحدد gRPC الاستجابات إلى 4MB. يقوم etcd بإزالة هذا القيد في SDK الذي يوفره ولكن نسي إزالته في gRPC-gateway. اتضح أن etcdctl الرسمي (المبني على SDK الذي يوفره) يعمل بشكل جيد، ولكن APISIX لا يعمل. انظر https://github.com/etcd-io/etcd/issues/12576.
- نفس المشكلة - هذه المرة مع الحد الأقصى لعدد الطلبات لنفس الاتصال. تنفيذ HTTP2 في Go يحتوي على تكوين
MaxConcurrentStreams
الذي يتحكم في عدد الطلبات المتزامنة التي يمكن أن يرسلها عميل واحد، ويتم تعيينه افتراضيًا إلى 250. أي عميل عادةً ما يرسل أكثر من 250 طلبًا في نفس الوقت؟ لذا فإن etcd دائمًا ما يستخدم هذا التكوين. ومع ذلك، فإن gRPC-gateway، "العميل" الذي يعمل كوسيط لجميع طلبات HTTP إلى واجهة gRPC المحلية، قد يتجاوز هذا الحد. انظر https://github.com/etcd-io/etcd/issues/14185. - بعد تمكين mTLS في etcd، يستخدم etcd نفس الشهادة كشهادة الخادم والشهادة العميل، شهادة الخادم لـ gRPC-gateway، والشهادة العميل عندما يصل gRPC-gateway إلى واجهة gRPC. إذا تم تمكين امتداد مصادقة الخادم على الشهادة، ولكن لم يتم تمكين امتداد مصادقة العميل، فسيؤدي ذلك إلى حدوث خطأ في التحقق من الشهادة. مرة أخرى، الوصول مباشرة باستخدام etcdctl يعمل بشكل جيد (لأن الشهادة لن تستخدم كشهادة عميل في هذه الحالة)، ولكن APISIX لا يعمل. انظر https://github.com/etcd-io/etcd/issues/9785.
- بعد تمكين mTLS، يسمح etcd بتكوين سياسات الأمان لمعلومات مستخدم الشهادات. كما ذكرنا أعلاه، يستخدم gRPC-gateway شهادة عميل ثابتة عند الوصول إلى واجهة gRPC بدلاً من معلومات الشهادة المستخدمة للوصول إلى واجهة HTTP في البداية. وبالتالي، لن تعمل هذه الميزة بشكل طبيعي لأن شهادة العميل ثابتة ولن يتم تغييرها. انظر https://github.com/apache/apisix/issues/5608.
يمكننا تلخيص المشاكل في نقطتين:
- gRPC-gateway (وربما محاولات أخرى لتحويل HTTP إلى gRPC) ليست حلًا سحريًا يحل جميع المشاكل.
- مطورو etcd لا يضعون تركيزًا كافيًا على طريقة HTTP. وأكبر مستخدم لهم، Kubernetes، لا يستخدم هذه الميزة.
نحن لا نتحدث عن مشاكل برنامج معين هنا، etcd هو مجرد مثال نموذجي يستخدم gRPC. جميع الخدمات التي تستخدم gRPC كإطار عمل RPC الرئيسي لها قيود مماثلة في دعمها لـ HTTP.
كيف يحل APISIX 3.0 هذه المشكلة
يقول المثل، "إذا لم تأتِ الجبل إلى محمد، فليذهب محمد إلى الجبل." إذا قمنا بتنفيذ عميل gRPC تحت OpenResty، يمكننا التواصل مباشرة مع خدمة gRPC.
بالنظر إلى عبء العمل والاستقرار، قررنا التطوير بناءً على مكتبة gRPC الشائعة بدلاً من إعادة اختراع العجلة. قمنا بفحص مكتبات gRPC التالية:
- خدمة gRPC الخاصة بـ NGINX. NGINX لا يعرض gRPC للمستخدمين الخارجيين، ولا حتى واجهة برمجة تطبيقات عالية المستوى. إذا كنت تريد استخدامها، يمكنك فقط نسخ بعض الوظائف منخفضة المستوى ثم دمجها في واجهة عالية المستوى. دمجها سيسبب عبء عمل إضافي.
- مكتبة gRPC الرسمية لـ C++. نظرًا لأن نظامنا يعتمد على NGINX، يمكن أن يكون دمج مكتبات C++ معقدًا بعض الشيء. بالإضافة إلى ذلك، فإن اعتماد هذه المكتبة يقترب من 2GB، مما سيكون تحديًا كبيرًا لبناء APISIX.
- التنفيذ الرسمي لـ gRPC في Go. لدى Go أدوات قوية، ويمكننا بناء المشاريع بسرعة فيها. ومع ذلك، من المؤسف أن أداء هذا التنفيذ بعيد عن نسخة C++. لذا نظرنا إلى تنفيذ آخر لـ Go: https://github.com/bufbuild/connect-go/. لسوء الحظ، أداء هذا المشروع ليس أفضل من النسخة الرسمية أيضًا.
- تنفيذ مكتبة gRPC في Rust. ستكون هذه المكتبة خيارًا طبيعيًا للجمع بين إدارة التبعيات والأداء. لسوء الحظ، نحن غير ملمين بـ Rust ولن نراهن عليها.
بالنظر إلى أن عمليات عميل gRPC هي في الأساس كلها مرتبطة بـ IO، فإن متطلبات الأداء ليست الأولوية. بعد التفكير بعناية، قمنا بتنفيذها بناءً على Go-gRPC.
لتنسيق مع مجدول Lua، قمنا بكتابة وحدة NGINX C: https://github.com/api7/grpc-client-nginx-module. في البداية، أردنا دمج كود Go في هذه الوحدة C من خلال تجميعه في مكتبة مرتبطة بشكل ثابت عبر cgo. ومع ذلك، وجدنا أنه نظرًا لأن Go هو تطبيق متعدد الخيوط، فإن العملية الفرعية لن ترث جميع الخيوط من العملية الأصلية بعد التفرع. لا توجد طريقة للتكيف مع بنية NGINX متعددة العمليات master-worker. لذا قمنا بتجميع كود Go في مكتبة مرتبطة ديناميكيًا (DLL) ثم قمنا بتحميلها في عملية العامل أثناء التشغيل.
قمنا بتنفيذ آلية قائمة انتظار المهام لتنسيق مجدولات Go مع مجدولات Lua. عندما يبدأ كود Lua عملية IO لـ gRPC، يقوم بإرسال مهمة إلى جانب Go ويعلق نفسه. ستقوم مجدولة Go بتنفيذ هذه المهمة، وسيتم كتابة نتيجة التنفيذ في قائمة الانتظار. يقوم خلفية خيط على جانب NGINX باستهلاك نتيجة تنفيذ المهمة، ويعيد جدولة مجدولة Lua المقابلة، ويستمر في تنفيذ كود Lua. بهذه الطريقة، عمليات IO لـ gRPC لا تختلف عن عمليات المقبس العادية في نظر كود Lua.
الآن، تم الانتهاء من معظم أعمال وحدة NGINX C. كل ما علينا فعله هو أخذ ملف .proto
الخاص بـ etcd (الذي يعرّف واجهة gRPC الخاصة به)، وتعديله، ثم تحميل الملف في Lua للحصول على عميل etcd التالي:
local gcli = require("resty.grpc")
assert(gcli.load("t/testdata/rpc.proto"))
local conn = assert(gcli.connect("127.0.0.1:2379"))
local st, err = conn:new_server_stream("etcdserverpb.Watch", "Watch",
{create_request =
{key = ngx.var.arg_key}},
{timeout = 30000})
if not st then
ngx.status = 503
ngx.say(err)
return
end
for i = 1, (ngx.var.arg_count or 10) do
local res, err = st:recv()
ngx.log(ngx.WARN, "received ", cjson.encode(res))
if not res then
ngx.status = 503
ngx.say(err)
break
end
end
هذا التنفيذ القائم على gRPC أفضل من lua-resty-etcd، وهو مشروع عميل etcd-HTTP يحتوي على 1600 سطر من الكود في Lua فقط.
بالطبع، ما زلنا بعيدين عن استبدال lua-resty-etcd. للاتصال الكامل مع etcd، تحتاج وحدة grpc-client-nginx-module أيضًا إلى إكمال الوظائف التالية:
- دعم mTLS
- دعم تكوين بيانات تعريف gRPC
- دعم تكوينات المعلمات (مثل
MaxConcurrentStreams
وMaxRecvMsgSize
) - دعم الطلبات من L4
لحسن الحظ، قمنا بإعداد الأساس، ودعم هذه الأشياء هو مجرد مسألة وقت.
سيتم دمج وحدة grpc-client-nginx-module في APISIX 3.0، ثم يمكن لمستخدمي APISIX استخدام طرق هذه الوحدة في ملحق APISIX للتواصل مباشرة مع خدمات gRPC.
مع الدعم الأصلي لـ gRPC، سيحصل APISIX على تجربة أفضل مع etcd ويفتح الباب أمام إمكانيات ميزات مثل فحص صحة gRPC وإعداد تقارير بيانات التتبع المفتوحة القائمة على gRPC.
نحن متحمسون لرؤية المزيد من الميزات القائمة على gRPC في APISIX في المستقبل!